ReactJS組件之間通信

itveryboy 8年前發布 | 19K 次閱讀 React JavaScript開發

最近在學習react.js,不得不說第一次接觸組件化開發很神奇,當然也很不習慣。

react的思想還是蠻獨特的,當然圍繞react的一系列自動化工具也是讓我感覺亞歷山大

今天總結一下react組件之間的通信,權當是自己的學習筆記:

reactJs中數據流向的的特點是:單項數據流

react組件之間的組合不知道為什么給我一種數據結構當中樹的感覺,數據就是從根節點(頂端或其他子樹的頂端)“流”下來,大概就是這個樣子:

比如這是一個組件樹,數據就可以從main組件流到jumbotron組件、queationList組件、form組件,類似的queation組件的數據也可以流到下邊的question組件里邊。

但是很遺憾,這個從上到下的數據流動,只能解決很少的問題,還有一部分的數據流動就是類似從jumbotron組件到form組件的這樣的兄弟組件的流動形式,或者隔著幾個層級的數據流動,再或者子組件發生了數據的變化,想通知父組件,數據流向從子組件到父組件流,這些問題才是大多數的開發者需要面臨的問題。

所以這篇筆記總結下基礎的組件通信:

數據從父組件到子組件

最簡單的通信就是父子間的通信,比如上邊圖上的有個jsonObj從main流進了QueationList

參考代碼:

//這里模擬出幾條數據
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')
);

代碼寫的不怎么規范,但是數據的傳遞就是這樣的:

<QuestionList jsonObj={jsonObj} />

這樣就可以把父組件的數據帶到子組件里邊

數據從子組件到父組件

理論上來說數據只能是單向的,所以不借助插件數據還真不好從子組件到父組件,一種很簡單的手段是回調函數:

在父組件當中寫個回調函數,然后傳遞到子組件,什么時候子組件數據變化了,直接調這個回調函數就可以了。

比如現在的jumbotron的按鈕被點擊了,我們想把被點擊這個事件發給它的父組件也就是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
        };
    },
    //開始給子組件一個回調函數,用來做子組件給父組件通信使用
    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')
);

子組件通知父組件狀態變化就是這樣,就好像是兒子找爸爸要零花錢,零花錢以及給不給都是爸爸說了算的。

兄弟組件之間的通信

這個其實應該是一個動態應用中最常見的通信,比如jubotron組件的點擊按鈕,form組件的表單出現:

這就是一個典型的兄弟之間的通信:

兄弟節點其實可以就是子父通信&&父子通信的疊加

首先按鈕被點擊,子組件通知負組件這個事件,然后父組件把這個消息帶給另一個子組件

下邊是個點擊按鈕顯示表單的例子:

/**
 * 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')
);

就是這樣,

其實上邊的代碼是我從之前的沒事干做的一個單頁面上拿過來改的,為了不出現代碼無法運行的問題,下邊貼出所有代碼:

/**
 * 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')
);

最后就是一個很重要的問題:就是多層級的據數據傳輸,如果還用這個方式來傳播的話,效率貌似是個大問題,解決辦法看大家的做法目前暫時還是flux之類的其他框架,等研究出來單獨寫篇文章吧

 

來自:https://segmentfault.com/a/1190000006897659

 

 本文由用戶 itveryboy 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!