大家好,初学react小白一枚,现在有个问题想请教大家,关于react组件间的通信问题。
举个例子,见图
ABCDE都是react组建,D是个button,现在想通过点击D控制E的显示与隐藏,之前的方法是在他们共同的父级设置一个state,写个函数可以改变这个state,然后A将这个函数一层一层传到D,在D里面写个回调,A同时会将state一层一层传到E。
不知道这种做法是否合理,如果D和E的层次更深,那个函数和state就需要传很多层,感觉很蛋疼。
有没有什么方法能够使D直接与E通信。
还请大家不吝赐教。
关于react组件间通信的问题
如果是不相关的两个独立的组件呢
比如下面的例子,左面是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')
);
不需要。我这边 store 还是在 parent component 监听。children 虽然直接从 store 获取 state ,但是一旦 store 变化, parent 会 re render,然后 children 也会 re render
我这里的情况是
state 由 A 管理
A 的 state 变化,会自动修改 B/C/D/E
但是 B 在 render 之后触发 了 B 的 componentDidUpdate,
然后又会修改A的state, 然后又进行 C 的 render, 但此时 C 之前的 render 还未结束
有什么分两次调用React.render的理由吗?
用一个父组件包起来这两个组件,然后类似Tree的控件改成
<TreeNode node={this.state.node}>
向另一个控件的props提供一个回调
<InputForm onSubmit={this.updateNode}>
在updateNode里面更新父组件的state就可以了吧
用两个主要是为了 input只做编辑, treeview只用来显示 input以后还可能会增加其他的很多数据域 全部集成在一起,界面操作会很复杂,我想页面左侧显示树形菜单,在 右面内容区显示编辑界面
子组件层次深不一定互动复杂,例如如果B和C中没有其他组件,在组件A中可以只关心B和C。D和E分别由B和C封装好就可以
如果与用户互动的逻辑也比较复杂,可以用更加模式化的方法
例如Flux或者immutable store
之前也是很纠结这个问题,查了一些资料参考:
- React官方文档communicate-between-components有提到,如果是父子关系的组件可以通过属性传递来实现通信,如果不是父子关系的话通过全局事件系统来实现。
- Stackoverflow上也有相关的讨论:《ReactJS Two components communicatin》、《Pass props to parent component in React.js》等(囧,不能贴多余两天链接,这里就不给了)
- How to communicate between React components,这位博主总结了React组件通信的问题:没有最好的解决方案,主要取决于自己的需求,应用的大小,组件的数量。
回复下自己,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.
一次 render 的过程中不应该有 setState 发生,如果是写一个 component 的话,react 会报此 error。
UI = f(x),中间不要对 x 产生 mutations。
如果工程上发现进了死胡同,可以详细讨论下为什么出现?是否可以通过设计避免。
anyway,参考我上面的回复,state is evil。目前我只有在一个 component 范围的数据采用 state,比如是否显示 modal 这种。
我一般用ScaleApp,D只负责从ScaleApp发消息,E只负责接受ScaleApp的消息。这样不用写那么多父组件,而且D和E组件可以重用,只要有这种消息订阅/发布的模式,就可以了。Flux和这个一样。