RN(四)--组件间通信
2018-01-03 本文已影响88人
doudo
谈组件通信前我们先了解一下组件的生命周期,这也是基础。
一、组件生命周期
我们先来看个图: 组件生命周期这个图看上图挺乱,其实我们注意一下经常会用到的就行:
- render中只做与渲染有关的操作,只读取数据,不修改数据
因为界面是经常变化的,render也是经常触发的。所以如果有修改数据的操作,就会多次触发,使结果难以预料。比如如果你在render中setState,就会触发render执行,而又会执行setState,导致死循环。
- 随组件加载只执行一次的操作,应该放在componentWillMount和componentDidMount中。如从网络获取首页数据,如果需要用到timer定时器的时候可以在这里初始化定时器。
- 记得在componentWillUnmount销毁定时器和一些订阅事件。
- props发生变化时,会触发componentWillReceiveProps,经常会在这个方法中将变动同步给state。
二、组件间通信
1. 父组件向子组件通信(子组件间通信也类似)
- 父组件以自身的state作为子组件的props。父组件调用setState修改state,触发父组件的render方法,于是子组件的props发生变化,此时子组件的生命周期方法componentWillReceiveProps会执行,在该方法中执行相应操作。
看代码就容易理解了:
//代码解释:父组件初始化一个时间time,父组件显示该值,并把这个初始值传给子组件显示;然后触摸会修改父组件的time值,并同步给子组件。
//子组件
class CountDown extends Component {
state = {
count: this.props.defaultTime,//state可以理解为静态,只初始化一次
};
render() {
const { count } = this.state;
return (
<Text>子:{this.state.count}</Text>
)
}
componentWillReceiveProps(nextProps){
this.setState({
count : nextProps.defaultTime,
});
}
componentWillUnmount() {
clearInterval(this.timer);
}
}
//父组件
class App extends Component {
state = {
time : 30,
}
press = ()=>{
const {time} = this.state;
this.setState({
time : time+10,
})
}
render() {
return (
<View>
<TouchableOpacity onPress={this.press}>
<Text>加10秒</Text>
</TouchableOpacity>
<CountDown defaultTime={this.state.time} />{/*render刷新时,会重用该组件*/}
<Text>父:{this.state.time}</Text>
</View>
)
}
}
- 使用ref调用子组件的方法
通过父组件通过ref将子组件的实例保存为父组件的成员变量,父组件就可以通过该成员变量直接执行子组件的方法了,可以把数据作为参数进行传递。ref后的参数即箭头函数的参数为该组件的实例。
class CountDown extends Component {
state = {
count: this.props.defaultTime,//state可以理解为静态,只初始化一次
};
render() {
const { count } = this.state;
return (
<Text>子:{this.state.count}</Text>
)
}
addTime = ()=>{
const {count} = this.state;
this.setState({
count : count+10,
})
}
}
class App extends Component {
state = {
time : 30,
}
press = ()=>{
//父组件自身数据的修改
const {time} = this.state;
this.setState({
time : time+10,
})
//直接调用子组件的方法
this.countDown.addTime(10);
}
render() {
return (
<View>
<TouchableOpacity onPress={this.press}>
<Text>加10秒</Text>
</TouchableOpacity>
{/*ref后的参数为该组件的实例*/}
<CountDown ref={ins=>this.countDown=ins} defaultTime={this.state.time} />
<Text>父:{this.state.time}</Text>
</View>
)
}
}
2. 子组件向父组件通信(回调函数)
父组件将函数作为子组件的props传递给子组件,子组件需要向父组件通信时,就调用传递过来的函数,数据作为函数的参数传回。
//子组件
class CountDown extends Component {
state = {
count: 5,
};
render() {
const { count } = this.state;
return (
<Text>{count}</Text>
)
}
componentDidMount() {
this.timer = setInterval(() => {
const { count } = this.state;
if (count === 0) {
this.props.timeUp && this.props.timeUp('aaa');//回调父组件的方法
return clearInterval(this.timer);
}
this.setState({
count: count - 1,
});
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timer);
}
}
//父组件
class App extends Component {
timeUpParent = (param)=>{
alert('时间到,参数:是'+param);
}
render() {
return (
<View>
<CountDown timeUp={this.timeUpParent} />
</View>
)
}
}
如果有多个子组件都会回调那怎么办呢?
那就需要给每个子组件传个参数,不过子组件并不用显式的回传,只需传自己的要传的参数,父组件在参数中就会收到之前自己传的值。这样说可能不好理解,不要紧,看代码就好理解了。
//子组件
class CountDown extends Component {
state = {
count: 5,
};
render() {
const { count } = this.state;
return (
<Text>{count}</Text>
)
}
componentDidMount() {
this.timer = setInterval(() => {
const { count } = this.state;
if (count === 0) {
this.props.timeUp && this.props.timeUp('子组件传递的参数:aaa');//对于子组件来说,他要传的参数是箭头函数前边的参数
return clearInterval(this.timer);
}
this.setState({
count: count - 1,
});
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timer);
}
}
//父组件
class App extends Component {
arr = ['1','2','3'];
timeUpParent = (param1,param2)=>{
alert('时间到'+'\n'+'param1是'+param1+'\n'+'param2是'+param2);
}
render() {
return (
<View>
{
this.arr.map(i=>{
{/*传给子组件的属性的为整个箭头函数,子组件的参数为箭头函数的参数*/}
return <CountDown key={i} timeUp={(childParam)=>{this.timeUpParent('父用于区分子组件的参数:'+i,childParam)}} />
})
}
</View>
)
}
}
3. 任意组件之间的通信
这些高级的东西,先暂时有个概念,知道是解决什么问题即可,先掌握以上的基础是当前之重。
- 全局时间订阅系统(EventEmitter)
- 单向数据流框架(Flux系)
flux、reflux、alt、redux - 双向数据流框架
mobx