1.父子元件
ReactJS中資料的流動是單向的
父元件傳遞資料給子元件: 可以通過設定子元件的props傳遞資料給子元件。
子元件改變父元件的資料: 可以在父元件中傳一個callback(回調函數)給子元件,子元件調用callback可改變父元件的資料。
執行個體:點選子元件,改變父元件的背景色
export default class SampleOne extends Component {
constructor(props) {
super(props);
this.state = {
color: 'white',
};
}
// 改變curItem的回調函數
_changeColor(color) {
this.setState({ color: color });
}
render() {
return (
<View style={{ backgroundColor: this.state.color, flex: 1 }}>
<Child text='Child one' color='red'
changeColor={ (color) => this._changeColor.bind(this, color) }/>
<Child text='Child two' color='white'
changeColor={ (color) => this._changeColor.bind(this, color) }/>
</View>
);
}
}
class Child extends Component {
render() {
return (
<Text style = {{ fontSize: 50, margin: 20 }}
onPress={this.props.changeColor(this.props.color) }>
{this.props.text}
</Text>
);
}
}
2.兄弟元件
當兩個元件不是父子關系,但有相同的父元件時,将這兩個元件稱為兄弟元件。
兄弟元件不能直接互相傳送資料,此時可以将資料挂載在父元件中。
由兩個元件共享:如果元件需要資料渲染,則由父元件通過props傳遞給該元件;
如果元件需要改變資料,則父元件傳遞一個改變資料的回調函數給該元件,并在對應事件中調用。
執行個體:點選子元件,改變兄弟元件的字型顔色
export default class SampleTwo extends Component {
constructor(props) {
super(props);
this.state = {
child1color: 'blue',
};
}
_changeItemColor(color) {
this.setState({ child1color: color });
}
render() {
return (
<View style={{flex: 1 }}>
<Child ref='child1' text='Child one' color={this.state.child1color}/>
<Child ref='child2' text='Child two' color='purple'
changeBrotherColor={ (color) => this._changeItemColor(color) }/>
</View>
);
}
}
class Child extends Component {
_changeBrotherColor() {
this.props.changeBrotherColor && this.props.changeBrotherColor('green');
}
render() {
return (
<Text style = {{ fontSize: 60, margin: 20, color: this.props.color }}
onPress={this._changeBrotherColor.bind(this)}>
{this.props.text}
</Text>
);
}
}
3.全局事件
1和2中的方法在組織結構非常深的時候,将會是一個噩夢。我們可以用全局事件來避免這些麻煩
使用事件來實作元件間的溝通:改變資料的元件發起一個事件,使用資料的元件監聽這個事件,在事件處理函數中觸發setState來改變視圖或者做其他的操作。
使用事件實作元件間溝通脫離了單向資料流機制,不用将資料或者回調函數一層一層地傳給子元件。
事件子產品可以使用如EventEmitter或PostalJS這些第三方庫,也可以自己簡單實作一個。
使用RCTDeviceEventEmitter sample code:
import RCTDeviceEventEmitter from 'RCTDeviceEventEmitter';
export default class SampleThree extends Component {
render() {
return (
<View style={styles.container}>
<ChildInput />
<ChildShow />
</View>
);
}
}
class ChildInput extends Component {
handleUpdateChange(text) {
RCTDeviceEventEmitter.emit('change', text);
}
render() {
return (
<View style={{ justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF' }}>
<TextInput onChangeText={(text) => this.handleUpdateChange(text) } style={{ width: 200, height: 40, borderColor: 'gray', borderWidth: 1 }} />
</View>
);
}
}
class ChildShow extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
};
}
componentDidMount() {
let me = this;
RCTDeviceEventEmitter.addListener('change', function (text) {
me.setState({
text: text
})
})
}
render() {
return (
<View style={{ justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF' }}>
<Text>{this.state.text}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
但是正因為全局事件脫離了單向資料流機制,進而使得資料的流向變得的不可預期。
4. Navigator didfocus 和 Navigator傳遞方法通訊
監聽didfocus事件(不推薦)
用在有Navigator的scenario.
在需要更新資料的元件didmount時重新load storage資料加載
關鍵code
componentWillMount() {
console.log('componentWillMount');
let me = this;
let willcallback = (event) => {
console.log('will enter ' + this.props.name);
};
let didcallback = (event) => {
console.log(
'didfocus event ',
{
route: event.data.route,
target: event.target,
type: event.type
});
// homeview did mount
if ('HomeView' === event.data.route.name && 'didfocus' === event.type) {
console.log('didfocus HomeView');
this.updatetext && me.setState({ text: this.updatetext });
}
};
// observe focus change events from this component
this._listeners = [
// 該會頁面每次進行切換之前調用
this._navigator.navigationContext.addListener('willfocus', willcallback),
// 在每次頁面切換完成或者初始化之後進行調用該方法。該參數為新頁面的路由
this._navigator.navigationContext.addListener('didfocus', didcallback),
];
}
Navigator傳遞方法
用在有Navigator的scenario
in SampleFive.js
_navigateToSub() {
console.log('_navigateToSub');
const self = this;
this._navigator && this._navigator.push({
name: 'SampleFiveSub',
component: SampleFiveSub,
params: {
updateText: (text) => {
self.updatetext = text;
}
}
});
}
in SampleFileSub.js
_back() {
console.log('SampleFiveSub _back');
this.props.updateText && this.props.updateText(this.text)
const {navigator} = this.props;
navigator && navigator.pop();
}
5. Redux
Redux可以檢視章節 Redux
小結
- 簡單的的元件溝通可以用傳props和callback的方法實作。随着項目規模的擴大,元件就會嵌套得越來越深,這時候使用這個方法就有點不太适合。
- 全局事件可以讓元件直接溝通,但頻繁使用事件會讓資料流動變得很亂。
- 簡單的處理邏輯也可以用Navigator傳遞callback方法
- 使用redux可以讓你整個項目的資料流向十厘清晰,但是很容易會出現元件嵌套太深的情況,events和context都可以解決這個問題。
- Transdux是一個類redux架構,使用這個架構可以寫出比redux簡潔的代碼,又可以得到redux的好處。
ReactJS元件間溝通的一些方法