ReactJS組件之間通信
最近在學習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