react梳理之组件通信(传值)
react组件的通信属于开发基础知识,今天来梳理一下,当然rudex还按老规矩排除在外,如同上篇文章的hooks一样,单独梳理一.react组件常用的通信情景父子组件通信兄弟组件通信(非父子组件)1.父子组件通信props传值props传值算是react中最基本的传值方式,如果使用或者学习过vue的会了解到,props也是vue的父子组件基本的传值方式,通过父组件使用子组件的时候绑定值,在子组件中
react组件的通信属于开发基础知识,今天来梳理一下,当然rudex还按老规矩排除在外,如同上篇文章的hooks一样,单独梳理
一.react组件常用的通信情景
- 父子组件通信
- 非父子组件通信
1.父子组件通信
- props通信
- props给子组件传值
props传值算是react中最基本的传值方式,如果使用或者学习过vue的会了解到,props也是vue的父子组件基本的传值方式,通过父组件使用子组件的时候绑定值,在子组件中接收props值来完成,props是一个对象,对象内接收了父组件通过键值对方式传递的值,但是在react中我们的传值接收必须使用this.props来接收,并没有vue中封装好的接收方式,这里有一个知识点就是:在vue中是父组件传值被改变的时候相应的props内的值也会被改变,从而触发子组件刷新,但是在react中并不是这样
,我们来看一个例子
import React from 'react';
class AAA extends React.Component {
render() {
return <div>{this.props.value}</div>;
}
}
class App extends React.Component {
constructor() {
super();
this.value = 222;
}
render() {
return (
<div>
<AAA value={this.value} />
<button
onClick={() => {
this.value = 333;
console.log(this.value);
}}
>
点击改变值
</button>
</div>
);
}
}
export default App;
我们真实运行这段代码,会发现一个问题,就是我们打印的数值value出现的是333,但是实际上我们页面展示的依旧是222,这是为什么呢,这就是因为我们实际上触发子组件更新的并不是props的改变,而是setState,我们在vue和react的的diff算法对比中有讲过,在react中,当更新父组件state中的数据时,父组件以及子组件和所有的子孙组件都会被更新,这个可以通过shouldComponentUpdate来优化解决,也就是说子组件是否更新是父组件的state决定的,并不是props决定的
- 子组件通知父组件修改值
子组件通知父组件修改值也与vue有所不同,在vue中我们会使用this.$emit去通知父组件通过@这种独特的方式绑定的方法,但是在react中并没有,我们只能通过this.props来调用,简单来讲props即承担了传递值的作用,也承担了传递方法的作用,我们来看一个例子
import React from 'react';
class AAA extends React.Component {
render() {
return (
<div>
{this.props.value}
<button
onClick={() => {
this.props.onClickEvent();
}}
>
点击改变值
</button>
</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
value: 222,
};
}
render() {
return (
<div>
<AAA
value={this.state.value}
onClickEvent={() => {
this.setState({
value: 333,
});
}}
/>
</div>
);
}
}
export default App;
通过这个例子我们可以看到,我们的在父组件中使用子组件的上边传递了一个函数,这个函数可以不叫onClickEvent,甚至可以叫任何名称,通过子组件button的onClick事件触发的时候,我们会通过this.props来调用父组件传递的函数,父组件中的函数也就会被执行
同时这里我们运行代码还可以看到我们使用setState之后,子组件的值也会被更新,也就验证了我们props更改实际上不会触发子组件更新的事实
- ref通信
通过给父组件使用子组件的地方添加ref,然后通过ref去调用子组件上的方法,因为ref就是子组件的实例嘛,所以肯定是可以访问到子组件的方法的,在子组件的方法里我们去通过调用的时候传递的参数接收父组件传递过来的值,能实现,但是不建议这样
ref肯定是非常有用的,但是用在这里反而有些杀鸡用牛刀的感觉,导致我们把代码变得更加复杂
简单看一下,大概就是这样的
//父组件中
render() {
return (
<div>
<AAA ref={this.myRef} />
</div>
);
}
//父组件传递参数
this.myRef.current.setProps({a:1});
//子组件中
setProps(props) {
//接收props
}
很low,但是你不能说这个传值方法不存在
1.非父子组件通信
- 状态提升
啥叫状态,这个我们前边的文章里有解释过,就是组件内的状态数据,state,就是状态,外部的props就是属性,属性是通过父组件去获取的,状态时组件内部自己的,那么状态提升跟状态有什么关系呢,其实没啥关系,状态提升了就变成啥了,变成父组件的状态了,也就变成当前组件的属性了,也就是把需要传值的状态,提升,提出去,提到外边的父组件里边去,这样保证兄弟组件都可以获取到这个’状态’,那也就达成了通信的目的
我们来看个例子
import React from 'react';
import './style.css';
class AAA extends React.Component {
render() {
return (
<div>
我是组件1我除了自己的逻辑,我还可以更改组件2获取的值
<button
onClick={() => {
this.props.onClickEvent('我是组件1传过来的值');
}}
>
点击改变值
</button>
</div>
);
}
}
class BBB extends React.Component {
render() {
return <div>我是组件2获取到的值:{this.props.value}</div>;
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
value: 222,
};
}
render() {
return (
<div>
<AAA
onClickEvent={(text) => {
this.setState({
value: text,
});
}}
/>
<BBB value={this.state.value} />
</div>
);
}
}
export default App;
我们发现我们两个组件AAA和BBB是兄弟组件,兄弟组件时常理来说应该是没什么交集的,唯一的交集就是都被父组件所使用,所以我们把原本BBB要使用的value值,'状态提升’到父组件中,那么AAA就可以调用父组件的方法去更改value 的值了,这样一来我们就可以在组件AAA处理逻辑或者进行什么操作的时候去更改value的值,也就是说可以传递自己想要传递的值了
- context
context是react内置的一种api或者说是组件,通过引用createContext来创建生产者和消费者,作用也很明显,生产者塞数据,消费者去使用,我们来看例子
import React from 'react';
import './style.css';
//createContext函数内可以传递一个默认的共享数据,我们用不到所以传
const ContextObj = React.createContext();
class AAA extends React.Component {
render() {
return (
<ContextObj.Consumer>
{(value) => (
<div>
我是组件1我除了自己的逻辑,我还可以更改组件2获取的值
<button
onClick={() => {
value.onClickEvent('333');
}}
>
点击改变值
</button>
</div>
)}
</ContextObj.Consumer>
);
}
}
class BBB extends React.Component {
render() {
return (
<ContextObj.Consumer>
{(value) => <div>我是组件2获取到的值:{value.name}</div>}
</ContextObj.Consumer>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
name: 222,
};
}
render() {
return (
<ContextObj.Provider
value={{
name: this.state.name,
onClickEvent: (name) => {
this.setState({
name: name,
});
},
}}
>
<div>
<AAA />
<BBB />
</div>
</ContextObj.Provider>
);
}
}
export default App;
从上述代码我们可以看到,我们的Provider可以看出是一个生产者,生产者传递了数据value这个对象,这里要说一下,
value是固定的并不是随意传值的嗷
,里边传递了我们所需的name,name是用在BBB中做展示使用的,我们还有一个东西被传递过去了,就是修改name的方法被传递过去了,从这里看很像props传值,因为props传值也是把方法一并传过去,然后用Consumer包裹我们要使用的子组件,子组件中接收的value中不但有name还有改变name的方法,这个方法也是在任意的组件中都可以调用的,也就是说,这个值还有更改值的能里,在每一个消费者那里都是可以使用的了,是不是有点像状态管理,实际上也就是一个微型的状态管理容器了,与props不同的是,我们可以在任何地方都去使用这个生产者消费者,只要是被Consumer包裹的消费者我们都不需传值可以直接使用,不管是孙子组件还是兄弟组件还是父子组件
1.订阅发布通信
这个通信实际上跟context一样,是适用于父子和非父子各种情况的,订阅者发布者模式我们在前边的设计模式梳理的观察者模式中有讲过,可以先看一下观察者模式梳理,看完这个我们带盖就可以知道,订阅发布的核心就是要有一个中枢机构,这个其实非常适合我们使用,因为我们可以把中枢机构封装到js中,然后再要发布或者订阅的组件中去引入,直接使用,订阅者通过添加订阅的方法去添加自己的方法(这里类比从子组件把自身的修改状态的方法传暴漏父组件),发布者通过发布方法调用一切的订阅者暴漏的修改自身状态的方法,实际使用情况就是当一个子组件去修改某个状态的时候,就可以通过发布者去调用所有的订阅者去同步
这里代码我就不举例了,订阅发布模式是讲过的,可以看看前边发过的观察者模式梳理的文章,顺便理解一下观察者与之的区别
总结,通信方式不讲redux,等到单独梳理redux,其他的通信方法(常用的)做了一个简单的梳理,再说一遍揩油就提到的一个不知道有用没用的盲点,就是
props在react中其实没有单独触发子组件更新的能力,只有setState才有
,而且这个子组件是就算没有props也会随着父组件更新,需要我们去对比props的状态是否改变,也是优化react刷新的一个方法
更多推荐
所有评论(0)