关于react组件间通信的问题

#1

大家好,初学react小白一枚,现在有个问题想请教大家,关于react组件间的通信问题。
举个例子,见图

ABCDE都是react组建,D是个button,现在想通过点击D控制E的显示与隐藏,之前的方法是在他们共同的父级设置一个state,写个函数可以改变这个state,然后A将这个函数一层一层传到D,在D里面写个回调,A同时会将state一层一层传到E。
不知道这种做法是否合理,如果D和E的层次更深,那个函数和state就需要传很多层,感觉很蛋疼。
有没有什么方法能够使D直接与E通信。
还请大家不吝赐教。

2 Likes
#2

ref

#3

如果我想做更复杂一些的操作,比如,E中有数据,点击D获取一些新数据渲染到E,ref是否可行

#4

注意语义就没问题了

#5

用flux store。

能用angular做到的,React都可以做到么?
#6

如果是不相关的两个独立的组件呢
比如下面的例子,左面是tree组件,右面是inputform 我想在inputform中输入省市,然后根据radio的分类添加到tree里面

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">

    
    <style>


input {


height:30px;
}

    </style>
<link rel="stylesheet" type="text/css" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.css" media="all" />
<link rel="stylesheet" type="text/css" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap-theme.min.css" media="all" />


 </head>

 <body>
 
       
<div class="container">

<div class="row">
  <div class="col-md-4">
             <div id="tree" class='panel' style="width:350px"></div>
  
  </div> 
  
  <div class="col-md-5">
       <div id="form" class='panel'  ></div>
  
   </div> 
   
 </div>  
  
</div>
  
<script src="http://cdn.bootcss.com/react/0.14.0-alpha1/react.min.js"></script>
<script src="http://cdn.bootcss.com/react/0.14.0-alpha1/JSXTransformer.js"></script>
 <script src="http://cdn.bootcss.com/react/0.14.0-alpha1/react-with-addons.min.js"></script>
   <script type="text/jsx" src="add.jsx"></script> 
 
 </body>
</html>

add.jsx


window.console={};
window.console.log=function(){};

var TreeNode = React.createClass({
  getInitialState: function() {
    return {
      visible: true
    };
  },
  render: function() {
    var childNodes;
    var classObj;

    if (this.props.node.childNodes != null) {
      childNodes = this.props.node.childNodes.map(function(node, index) {
        return <li key={index}><TreeNode node={node} /></li>
      });

      classObj = {
        togglable: true,
        "togglable-down": this.state.visible,
        "togglable-up": !this.state.visible
      };
    }

    var style;
    if (!this.state.visible) {
      style = {display: "none"};
    }

    return (
      <div>
        <h5 onClick={this.toggle}  >
          {this.props.node.title}
        </h5>
        <ul style={style}>
          {childNodes}
        </ul>
      </div>
    );
  },
  toggle: function() {
    this.setState({visible: !this.state.visible});
  }
});

var tree = {
  title: "中国",
  childNodes: [
    {title: "河北"},
    {title: "山东", childNodes: [
      {title: "济南", childNodes: [
        {title: "泉城路"}
      ]},
      {title: "泰安"}
    ]}
  ]
};

React.render(
  <TreeNode node={tree} />,
  document.getElementById("tree")
);




      
   var InputForm = React.createClass({
  render: function() {
    return (
      <form >
      <div  className="form-group" >
      <label >级别 
         <input type="radio" name='l' value="省" /> 省
          <input type="radio" name='l' checked value="市"/>  市
         <input type="radio" name='l' value="县"/> 县
         </label>
           </div>
            <div  className="form-group" >
      <label >名字  
         
        <input type="text" placeholder="value"  class="form-control" />
        <br/>
        <input type="submit" value="确定" className="btn-info" />
        </label>
        </div>
      </form>
    );
  }
});


      React.render(
        <InputForm/>,
        document.getElementById('form')
      );
      

#7

用flux架构,是否还需要在父级A设置state以用来更新E?

#8

不需要。我这边 store 还是在 parent component 监听。children 虽然直接从 store 获取 state ,但是一旦 store 变化, parent 会 re render,然后 children 也会 re render

1 Like
#10

我这里的情况是

state 由 A 管理
A 的 state 变化,会自动修改 B/C/D/E

但是 B 在 render 之后触发 了 B 的 componentDidUpdate,
然后又会修改A的state, 然后又进行 C 的 render, 但此时 C 之前的 render 还未结束
:grimacing:

#11

有什么分两次调用React.render的理由吗?

用一个父组件包起来这两个组件,然后类似Tree的控件改成

<TreeNode node={this.state.node}>

向另一个控件的props提供一个回调

<InputForm onSubmit={this.updateNode}>

在updateNode里面更新父组件的state就可以了吧

#12

用两个主要是为了 input只做编辑, treeview只用来显示 input以后还可能会增加其他的很多数据域 全部集成在一起,界面操作会很复杂,我想页面左侧显示树形菜单,在 右面内容区显示编辑界面

#13

如果没有特别需求,一个React应用调用一次React.render就可以,父组件会递归地调用子组件的render。
只需要手动渲染根组件,比如叫<App/>

#14

我想问一下,如果treeview和input所处的层次像上边那个图那样,或者层次更深,那么这个state需要一层一层传下去吗?

1 Like
#15

子组件层次深不一定互动复杂,例如如果B和C中没有其他组件,在组件A中可以只关心B和C。D和E分别由B和C封装好就可以
如果与用户互动的逻辑也比较复杂,可以用更加模式化的方法
例如Flux或者immutable store

#16

之前也是很纠结这个问题,查了一些资料参考:

  1. React官方文档communicate-between-components有提到,如果是父子关系的组件可以通过属性传递来实现通信,如果不是父子关系的话通过全局事件系统来实现。
  2. Stackoverflow上也有相关的讨论:《ReactJS Two components communicatin》、《Pass props to parent component in React.js》等(囧,不能贴多余两天链接,这里就不给了)
  3. How to communicate between React components,这位博主总结了React组件通信的问题:没有最好的解决方案,主要取决于自己的需求,应用的大小,组件的数量。
#17

http://segmentfault.com/a/1190000002878442?utm_source=Weibo&utm_medium=shareLink&utm_campaign=socialShare

用这个也不错
虽然感觉有点不OOP,但是官方的东西应该有道理吧

#18

这种只适合相邻的组件,如果是互相独立的组件, 或者爷爷和孙子节点之间通信就不行了

#19

回复下自己,children 似乎不应该从 store 获取数据,而是从 parent(也就是 owner,当然 parent-based context 和 owner based context 区别不是很清楚,等待 0.14) 获取,通过 props。参考这篇文章:

[Why FluxComponent > fluxMixin] (https://github.com/acdlite/flummox/blob/v3.5.1/docs/docs/guides/why-flux-component-is-better-than-flux-mixin.md)

On an even more practical level, every time the state of a component changes, the entire component sub-tree is re-rendered. In our example from the previous section, BlogPostPage updates every time the posts store changes — including SiteNavigation, SiteSidebar, and SiteFooter, which don't need to re-render. Only BlogPost does. Imagine if you're listening to more than just one store. The problem is compounded.
#20

一次 render 的过程中不应该有 setState 发生,如果是写一个 component 的话,react 会报此 error。

UI = f(x),中间不要对 x 产生 mutations。

如果工程上发现进了死胡同,可以详细讨论下为什么出现?是否可以通过设计避免。

anyway,参考我上面的回复,state is evil。目前我只有在一个 component 范围的数据采用 state,比如是否显示 modal 这种。

#21

我一般用ScaleApp,D只负责从ScaleApp发消息,E只负责接受ScaleApp的消息。这样不用写那么多父组件,而且D和E组件可以重用,只要有这种消息订阅/发布的模式,就可以了。Flux和这个一样。