ReactJS组件之间通信
itveryboy
8年前
<p>最近在学习react.js,不得不说第一次接触组件化开发很神奇,当然也很不习惯。</p> <p>react的思想还是蛮独特的,当然围绕react的一系列自动化工具也是让我感觉亚历山大</p> <p>今天总结一下react组件之间的通信,权当是自己的学习笔记:</p> <p>reactJs中数据流向的的特点是:单项数据流</p> <p>react组件之间的组合不知道为什么给我一种数据结构当中树的感觉,数据就是从根节点(顶端或其他子树的顶端)“流”下来,大概就是这个样子:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/9fdd79a55a58bba4c82f8d7a12933135.jpg"></p> <p>比如这是一个组件树,数据就可以从main组件流到jumbotron组件、queationList组件、form组件,类似的queation组件的数据也可以流到下边的question组件里边。</p> <p>但是很遗憾,这个从上到下的数据流动,只能解决很少的问题,还有一部分的数据流动就是类似从jumbotron组件到form组件的这样的兄弟组件的流动形式,或者隔着几个层级的数据流动,再或者子组件发生了数据的变化,想通知父组件,数据流向从子组件到父组件流,这些问题才是大多数的开发者需要面临的问题。</p> <p>所以这篇笔记总结下基础的组件通信:</p> <h2><strong>数据从父组件到子组件</strong></h2> <p style="text-align:center"><img src="https://simg.open-open.com/show/2fd3b863fc876099cd7ebbe103a7f1b0.png"></p> <p>最简单的通信就是父子间的通信,比如上边图上的有个jsonObj从main流进了QueationList</p> <p>参考代码:</p> <pre> <code class="language-javascript">//这里模拟出几条数据 var jsonObj=[ {name:"A",question:"从小被人打怎么办?",TextArea:"习惯就好了",applaud:35,disagree:1}, {name:"B",question:"长的太帅被人砍怎么办?",TextArea:"食屎啦你",applaud:35,disagree:10}, {name:"C",question:"因为太胖被人摸怎么办?",TextArea:"享受就好了",applaud:35,disagree:45}, {name:"D",question:"被老师打不开心",TextArea:"用钱打脸",applaud:35,disagree:6}, {name:"E",question:"不爱洗澡怎么办?",TextArea:"打一顿就好了",applaud:35,disagree:9} ] var QuestionList=React.createClass({ prepareToRender:function(list){ var array=[]; for(var i=0;i<list.length;i++){ array.push(<Question obj={list[i]} key={i}/>); } return array; }, render:function(){ var array=this.prepareToRender(this.props.jsonObj); return <div>{array}</div>; } }); var Main = React.createClass({ //开始渲染 render: function () { return ( <div> <div className="container col-md-6 col-md-offset-3"> <div className="container-fluid"> <QuestionList jsonObj={jsonObj} /> </div> </div> ); } }); ReactDOM.render( <Main />, document.getElementById('container') );</code></pre> <p>代码写的不怎么规范,但是数据的传递就是这样的:</p> <p><QuestionList jsonObj={jsonObj} /></p> <p>这样就可以把父组件的数据带到子组件里边</p> <h2><strong>数据从子组件到父组件</strong></h2> <p>理论上来说数据只能是单向的,所以不借助插件数据还真不好从子组件到父组件,一种很简单的手段是回调函数:</p> <p>在父组件当中写个回调函数,然后传递到子组件,什么时候子组件数据变化了,直接调这个回调函数就可以了。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/bc85b9352588165d505afadc5739b183.png"></p> <p>比如现在的jumbotron的按钮被点击了,我们想把被点击这个事件发给它的父组件也就是main组件,那么我们可以这个做:</p> <pre> <code class="language-javascript">var Jumbotron = React.createClass({ handleClick: function () { this.props.openTheWindow(false); }, render: function () { return ( <div className="row"> <div className="col-md-6 col-md-offset-3"> <button type="button" className="btn btn-default btn-lg" onClick={this.handleClick}>开始体验 </button> </div> </div> </div> ); } }); var Main = React.createClass({ getInitialState: function () { return { openTheWindow: true }; }, //开始给子组件一个回调函数,用来做子组件给父组件通信使用 buttonResponse:function(windowSatus){ this.setState({openTheWindow : windowSatus}); }, //开始渲染 render: function () { console.log(jsonObj) return ( <div> <Jumbotron openTheWindow={this.buttonResponse}/> </div> ); } }); ReactDOM.render( <Main />, document.getElementById('container') );</code></pre> <p>子组件通知父组件状态变化就是这样,就好像是儿子找爸爸要零花钱,零花钱以及给不给都是爸爸说了算的。</p> <h2><strong>兄弟组件之间的通信</strong></h2> <p>这个其实应该是一个动态应用中最常见的通信,比如jubotron组件的点击按钮,form组件的表单出现:</p> <p>这就是一个典型的兄弟之间的通信:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/11a209a4310429231e0d70f4316188e0.png"></p> <p>兄弟节点其实可以就是子父通信&&父子通信的叠加</p> <p>首先按钮被点击,子组件通知负组件这个事件,然后父组件把这个消息带给另一个子组件</p> <p>下边是个点击按钮显示表单的例子:</p> <pre> <code class="language-javascript">/** * Created by niuGuangzhe on 2016/9/10. */ var Jumbotron = React.createClass({ handleClick: function () { this.props.openTheWindow(false); }, render: function () { return ( <div className="jumbotron"> <div className="row"> <div className="col-md-6 col-md-offset-3"> <button type="button" className="btn btn-default btn-lg" onClick={this.handleClick}>开始体验 </button> </div> </div> </div> ); } }); var Form = React.createClass({ getInitialState:function(){ return { inputTitle:"请输入标题", mainBody:"在此输入正文" }; }, //点击按钮触发事件:清除所有已经输入的文字 cleanText:function(){ this.setState({ inputTitle:"", mainBody:""}); }, //表单监视事件 handleChange(name,e) { var newState = {}; console.log(name); newState[name] =event.target.value; this.setState(newState); }, render: function () { return ( <div style={{display:this.props.openTheWindow?"none":"block"}}> <form role="form"> <div className="form-group"> <label htmlFor="title">标题</label> <input type="text" className="form-control" id="title" name="inputTitle" value={this.state.inputTitle} onChange={this.handleChange.bind(this,"inputTitle")}/> </div> <div className="form-group"> <label htmlFor="textArea">正文</label> <textarea className="form-control" rows="3" id="textArea" name="mainBody" value={this.state.mainBody} onChange={this.handleChange.bind(this,"mainBody")}></textarea> </div> <div className="row"> <input type="button" className="btn btn-default pull-right leaveMeAlone" value="取消" onClick={this.cleanText}/> <input type="submit" className="btn btn-primary pull-right leaveMeAlone" value="提交"/> </div> </form> </div> ) } }) var Main = React.createClass({ getInitialState: function () { return { openTheWindow: true }; }, //开始给子组件一个回调函数,用来做子组件给父组件通信使用 buttonResponse:function(windowSatus){ this.setState({openTheWindow : windowSatus}); }, //开始渲染 render: function () { console.log(jsonObj) return ( <div> <Jumbotron openTheWindow={this.buttonResponse}/> <div className="container col-md-6 col-md-offset-3"> <Form openTheWindow={this.state.openTheWindow}/> </div> </div> ); } }); ReactDOM.render( <Main />, document.getElementById('container') );</code></pre> <p>就是这样,</p> <p>其实上边的代码是我从之前的没事干做的一个单页面上拿过来改的,为了不出现代码无法运行的问题,下边贴出所有代码:</p> <pre> <code class="language-javascript">/** * Created by niuGuangzhe on 2016/9/10. */ var Jumbotron = React.createClass({ handleClick: function () { this.props.openTheWindow(false); }, render: function () { return ( <div className="jumbotron"> <div className="row"> <div className="col-md-6 col-md-offset-3"> <h2 className="colorChange">React+bootstrap简单实例</h2> </div> </div> <div className="row"> <div className="col-md-6 col-md-offset-3"> <p className="colorChange">上手体验:第一次尝试组件化开发</p> </div> </div> <div className="row"> <div className="col-md-6 col-md-offset-3"> <button type="button" className="btn btn-default btn-lg" onClick={this.handleClick}>开始体验 </button> </div> </div> </div> ); } }); var Form = React.createClass({ getInitialState:function(){ return { inputTitle:"请输入标题", mainBody:"在此输入正文" }; }, //点击按钮触发事件:清除所有已经输入的文字 cleanText:function(){ this.setState({ inputTitle:"", mainBody:""}); }, //表单监视事件 handleChange(name,e) { var newState = {}; console.log(name); newState[name] =event.target.value; this.setState(newState); }, render: function () { return ( <div style={{display:this.props.openTheWindow?"none":"block"}}> <form role="form"> <div className="form-group"> <label htmlFor="title">标题</label> <input type="text" className="form-control" id="title" name="inputTitle" value={this.state.inputTitle} onChange={this.handleChange.bind(this,"inputTitle")}/> </div> <div className="form-group"> <label htmlFor="textArea">标题</label> <textarea className="form-control" rows="3" id="textArea" name="mainBody" value={this.state.mainBody} onChange={this.handleChange.bind(this,"mainBody")}></textarea> </div> <div className="row"> <input type="button" className="btn btn-default pull-right leaveMeAlone" value="取消" onClick={this.cleanText}/> <input type="submit" className="btn btn-primary pull-right leaveMeAlone" value="提交"/> </div> </form> </div> ) }, //监测从新渲染 componentDidUpdate:function(){ console.log("子组件重新渲染;"); } }) var Question = React.createClass({ getInitialState : function(){ return { click:true, disClick:true }; }, numberHandle:function(){ if(this.state.click===true){ //奇数次点击,开始增加数据 this.props.obj.applaud+=1; this.setState({click:false}); }else{ //偶数次点击,减去数据 this.props.obj.applaud-=1; this.setState({click:true}); } }, decreateHandle:function(){ if(this.state.disClick===true){ //奇数次点击,开始增加数据 this.props.obj.applaud-=1; this.setState({disClick:false}); }else{ //偶数次点击,减去数据 this.props.obj.applaud+=1; this.setState({disClick:true}); } }, render: function () { return ( <div className="row leaveMe"> <div className="col-md-2"> <div className="col-md-12"> <button className="btn col-md-12 " onClick={this.numberHandle}>{this.props.obj.applaud-this.props.obj.disagree}<br/><span className="glyphicon glyphicon-chevron-up"></span></button> </div> <span> </span> <div className="col-md-12"> <button className="btn col-md-12" onClick={this.decreateHandle}><span className="glyphicon glyphicon-chevron-down"></span> </button> </div> </div> <div className="col-md-10 bs-callout bs-callout-info"> <h4>{this.props.obj.question}</h4> <p>{this.props.obj.TextArea}</p> </div> </div> ); } }); var QuestionList=React.createClass({ prepareToRender:function(list){ var array=[]; for(var i=0;i<list.length;i++){ array.push(<Question obj={list[i]} key={i}/>); } return array; }, render:function(){ var array=this.prepareToRender(this.props.jsonObj); return <div>{array}</div>; } }); //这里模拟出几条数据 var jsonObj=[ {name:"A",question:"从小被人打怎么办?",TextArea:"习惯就好了",applaud:35,disagree:1}, {name:"B",question:"长的太帅被人砍怎么办?",TextArea:"食屎啦你",applaud:35,disagree:10}, {name:"C",question:"因为太胖被人摸奶怎么办?",TextArea:"享受就好了",applaud:35,disagree:45}, {name:"D",question:"被老师打不开心",TextArea:"用钱打ta脸",applaud:35,disagree:6}, {name:"E",question:"不爱洗澡怎么办?",TextArea:"打一顿就好了",applaud:35,disagree:9} ] var Main = React.createClass({ getInitialState: function () { return { openTheWindow: true }; }, //开始给子组件一个回调函数,用来做子组件给父组件通信使用 buttonResponse:function(windowSatus){ this.setState({openTheWindow : windowSatus}); }, //开始渲染 render: function () { console.log(jsonObj) return ( <div> <Jumbotron openTheWindow={this.buttonResponse}/> <div className="container col-md-6 col-md-offset-3"> <Form openTheWindow={this.state.openTheWindow}/> <br/><br/> <div className="container-fluid"> <QuestionList jsonObj={jsonObj} /> </div> </div> </div> ); }, // 执行hook函数:重新渲染完成的时候调这个函数 componentDidUpdate:function(){ console.log(this.state.openTheWindow); } }); ReactDOM.render( <Main />, document.getElementById('container') );</code></pre> <p>最后就是一个很重要的问题:就是多层级的据数据传输,如果还用这个方式来传播的话,效率貌似是个大问题,解决办法看大家的做法目前暂时还是flux之类的其他框架,等研究出来单独写篇文章吧</p> <p> </p> <p>来自:https://segmentfault.com/a/1190000006897659</p> <p> </p>