ReactJS組件之間如何進行通信
最近在學(xué)習(xí)react.js,不得不說***次接觸組件化開發(fā)很神奇,當然也很不習(xí)慣。
react的思想還是蠻獨特的,當然圍繞react的一系列自動化工具也是讓我感覺亞歷山大
今天總結(jié)一下react組件之間的通信,權(quán)當是自己的學(xué)習(xí)筆記:
reactJs中數(shù)據(jù)流向的的特點是:單項數(shù)據(jù)流
react組件之間的組合不知道為什么給我一種數(shù)據(jù)結(jié)構(gòu)當中樹的感覺,數(shù)據(jù)就是從根節(jié)點(頂端或其他子樹的頂端)“流”下來,大概就是這個樣子:
比如這是一個組件樹,數(shù)據(jù)就可以從main組件流到j(luò)umbotron組件、queationList組件、form組件,類似的queation組件的數(shù)據(jù)也可以流到下邊的question組件里邊。
但是很遺憾,這個從上到下的數(shù)據(jù)流動,只能解決很少的問題,還有一部分的數(shù)據(jù)流動就是類似從jumbotron組件到form組件的這樣的兄弟組件的流動形式,或者隔著幾個層級的數(shù)據(jù)流動,再或者子組件發(fā)生了數(shù)據(jù)的變化,想通知父組件,數(shù)據(jù)流向從子組件到父組件流,這些問題才是大多數(shù)的開發(fā)者需要面臨的問題。所以這篇筆記總結(jié)下基礎(chǔ)的組件通信:
數(shù)據(jù)從父組件到子組件
最簡單的通信就是父子間的通信,比如上邊圖上的有個jsonObj從main流進了QueationList參考代碼:
- //這里模擬出幾條數(shù)據(jù)
- var jsonObj=[
- {name:"A",question:"從小被人打怎么辦?",TextArea:"習(xí)慣就好了",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')
- );
代碼寫的不怎么規(guī)范,但是數(shù)據(jù)的傳遞就是這樣的:
<QuestionList jsonObj={jsonObj} />
這樣就可以把父組件的數(shù)據(jù)帶到子組件里邊
數(shù)據(jù)從子組件到父組件
理論上來說數(shù)據(jù)只能是單向的,所以不借助插件數(shù)據(jù)還真不好從子組件到父組件,一種很簡單的手段是回調(diào)函數(shù):
在父組件當中寫個回調(diào)函數(shù),然后傳遞到子組件,什么時候子組件數(shù)據(jù)變化了,直接調(diào)這個回調(diào)函數(shù)就可以了。
比如現(xiàn)在的jumbotron的按鈕被點擊了,我們想把被點擊這個事件發(fā)給它的父組件也就是main組件,那么我們可以這個做:
- 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
- };
- },
- //開始給子組件一個回調(diào)函數(shù),用來做子組件給父組件通信使用
- 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')
- );
子組件通知父組件狀態(tài)變化就是這樣,就好像是兒子找爸爸要零花錢,零花錢以及給不給都是爸爸說了算的。
兄弟組件之間的通信
這個其實應(yīng)該是一個動態(tài)應(yīng)用中最常見的通信,比如jubotron組件的點擊按鈕,form組件的表單出現(xiàn):
這就是一個典型的兄弟之間的通信:
兄弟節(jié)點其實可以就是子父通信&&父子通信的疊加
首先按鈕被點擊,子組件通知負組件這個事件,然后父組件把這個消息帶給另一個子組件
下邊是個點擊按鈕顯示表單的例子:
- /**
- * 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:"在此輸入正文"
- };
- },
- //點擊按鈕觸發(fā)事件:清除所有已經(jīng)輸入的文字
- cleanText:function(){
- this.setState({
- inputTitle:"",
- mainBody:""});
- },
- //表單監(jiān)視事件
- 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
- };
- },
- //開始給子組件一個回調(diào)函數(shù),用來做子組件給父組件通信使用
- 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')
- );
就是這樣,
其實上邊的代碼是我從之前的沒事干做的一個單頁面上拿過來改的,為了不出現(xiàn)代碼無法運行的問題,下邊貼出所有代碼:
- /**
- * 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">上手體驗:***次嘗試組件化開發(fā)</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:"在此輸入正文"
- };
- },
- //點擊按鈕觸發(fā)事件:清除所有已經(jīng)輸入的文字
- cleanText:function(){
- this.setState({
- inputTitle:"",
- mainBody:""});
- },
- //表單監(jiān)視事件
- 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>
- )
- },
- //監(jiān)測從新渲染
- componentDidUpdate:function(){
- console.log("子組件重新渲染;");
- }
- })
- var Question = React.createClass({
- getInitialState : function(){
- return {
- click:true,
- disClick:true
- };
- },
- numberHandle:function(){
- if(this.state.click===true){
- //奇數(shù)次點擊,開始增加數(shù)據(jù)
- this.props.obj.applaud+=1;
- this.setState({click:false});
- }else{
- //偶數(shù)次點擊,減去數(shù)據(jù)
- this.props.obj.applaud-=1;
- this.setState({click:true});
- }
- },
- decreateHandle:function(){
- if(this.state.disClick===true){
- //奇數(shù)次點擊,開始增加數(shù)據(jù)
- this.props.obj.applaud-=1;
- this.setState({disClick:false});
- }else{
- //偶數(shù)次點擊,減去數(shù)據(jù)
- 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>;
- }
- });
- //這里模擬出幾條數(shù)據(jù)
- var jsonObj=[
- {name:"A",question:"從小被人打怎么辦?",TextArea:"習(xí)慣就好了",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
- };
- },
- //開始給子組件一個回調(diào)函數(shù),用來做子組件給父組件通信使用
- 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>
- );
- },
- // 執(zhí)行hook函數(shù):重新渲染完成的時候調(diào)這個函數(shù)
- componentDidUpdate:function(){
- console.log(this.state.openTheWindow);
- }
- });
- ReactDOM.render(
- <Main />,
- document.getElementById('container')
- );
***就是一個很重要的問題:就是多層級的據(jù)數(shù)據(jù)傳輸,如果還用這個方式來傳播的話,效率貌似是個大問題,解決辦法看大家的做法目前暫時還是flux之類的其他框架,等研究出來單獨寫篇文章吧