logo头像
游魂的网络日志

React组件通讯

本文于393天之前发表,文中内容可能已经过时。

组件通讯-概念

组件状态是独立的,组件化之后涉及状态同步,需要进行组件通讯

组件的特点

  • 组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据
  • 在组件化过程中,通常会将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能

组件通讯意义

  • 而在这个过程中,多个组件之间不可避免的要共享某些数据
  • 为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通
  • 这个过程就是组件通讯

props 基本使用

传递数据和接收数据的过程

  • 使用组件的时候通过属性绑定数据,在组件内部通过 props 获取即可。

函数组件使用 props

1
2
// 使用组件
<Hello name="jack" age="20" />
1
2
3
4
// 定义组件 props包含{name:'jack',age:'20'}
function Hello(props) {
return <div>接收到数据:{props.name}</div>;
}

类组件使用 props

1
2
// 使用组件
<Hello name="jack" age="20" />
1
2
3
4
5
6
// 定义组件 props包含{name:'jack',age:'20'}
class Hello extends Component {
render() {
return <div>接收到的数据:{this.props.age}</div>;
}
}

props 注意事项

什么是单向数据流

  • 单向数据流,是从上到下的,自顶而下的,数据流。
  • 好比:河流,瀑布,只能从上往下流动,上游污染下游受影响,但是下游不能影响上游。
  • 父组件传递数据给子组件,父组件更新数据子组件自动接收更新后数据,但是子组件是不能修改数据的。

props 可以传递什么数据?

  • 字符串
  • 数字
  • 布尔
  • 数组
  • 对象
  • 函数
  • JSX (插槽)
  • …任意

父传子方式

大致步骤:

  • 父组件提供要传递的 state 数据
  • 给子组件标签添加属性,值为 state 中的数据
  • 子组件中通过 props 接收父组件中传递的数据

父组件提供要传递的 state 数据

1
2
3
4
5
6
7
8
9
10
11
12
class Parent extends React.Component {
state = {
money: 10000,
};
render() {
return (
<div>
<h1>父组件:{this.state.money}</h1>
</div>
);
}
}

给子组件标签添加属性,值为 state 中的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
class Parent extends React.Component {
state = {
money: 10000
}
render() {
return (
<div>
<h1>父组件:{this.state.money}</h1>
+ <Child money={this.state.money} />
</div>
)
}
}

子组件中通过 props 接收父组件中传递的数据

1
2
3
4
5
6
7
function Child(props) {
return (
<div>
<h3>子组件:{props.money}</h3>
</div>
);
}

子传父方式

  • 父组件提供回调函数,通过 props 传递给子组件
  • 子组件调用 props 中的回调函数,函数可传参
  • 父组件函数的参数就是子组件传递的数据

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Parent extends React.Component {
state = {
money: 10000,
};
// 回调函数
buyPhone = (price) => {
this.setState({
money: this.state.money - price,
});
};
render() {
const { money } = this.state;
return (
<div>
<h1>父组件:{money}</h1>
<Child money={money} buyPhone={this.buyPhone} />
</div>
);
}
}

子组件

1
2
3
4
5
6
7
8
9
10
11
12
const Child = (props) => {
const handleClick = () => {
// 子组件调用父组件传递过来的回调函数
props.buyPhone(5000);
};
return (
<div>
<h3>子组件:{props.money}</h3>
<button onClick={handleClick}>买手机</button>
</div>
);
};

兄弟组件通讯

通过状态提升思想完成兄弟组件数据通讯

状态提升思想是什么?

  • 将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态和修改状态的方法
  • 需要通讯的组件通过 props 接收状态和函数即可

1638944148017.c33b506e

参考代码:

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

// 导入两个子组件
import Jack from './Jack';
import Rose from './Rose';

// App 是父组件
class App extends Component {
// 1. 状态提升到父组件
state = {
msg: '',
};

changeMsg = (msg) => {
this.setState({ msg });
};

render() {
return (
<div>
<h1>我是App组件</h1>
{/* 兄弟组件 1 */}
<Jack changeMsg={this.changeMsg}></Jack>
{/* 兄弟组件 2 */}
<Rose msg={this.state.msg}></Rose>
</div>
);
}
}

// 渲染组件
ReactDOM.render(<App />, document.getElementById('root'));

Jack.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, { Component } from 'react';

export default class Jack extends Component {
say = () => {
// 修改数据
this.props.changeMsg('you jump i look');
};
render() {
return (
<div>
<h3>我是Jack组件</h3>
<button onClick={this.say}>说</button>
</div>
);
}
}

Rose.jsx

1
2
3
4
5
6
7
8
9
10
11
import React, { Component } from 'react';

export default class Rose extends Component {
render() {
return (
<div>
<h3>我是Rose组件-{this.props.msg}</h3>
</div>
);
}
}

context 跨级组件通讯

什么是跨级组件通讯?

  • 组件间相隔多层,理解成叔侄,甚至更远的亲戚。

context 怎么去理解?

  • 术语:上下文
  • 理解:一个范围,只要在这个范围内,就可以跨级组件通讯。(不需要 props 层层传递)

1638932620946.11b91fb0

context 使用方法

  • 创建上下文

context.js

1
2
import { createContext } from 'react' // 引入创建上下文的方法
export default createContext(初始值) // 初始值会在找不到Provider提供者时生效
  • 使用上下文的Provider组件作为提供者
1
2
3
4
5
6
7
8
9
10
11
import Context from './context'
import Child from './Child';

function Parent () {
return (
// Provider包裹确定上下文生效范围,value注入范围内可用的数据
<Context.Provider value={context共享的值}>
....子孙组件
</Context.Provider>
)
}
  • 使上下文的Consumer作为消费者
1
2
3
4
5
6
7
8
9
10
import Context from './context'
function Child () {
return (
<Context.Consumer>
{
value => JSX
}
</Context.Consumer>
)
}
  • 示例

index.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React, { Component, createContext } from 'react'
import Parent from './Parent'

// 1. 创建上下文对象
// @ts-ignore
export const MyContext = createContext()

export default class App extends Component {
state = {
money: 10000
}
updateMoney = newMoney => {
this.setState({
money: newMoney
})
}
render() {
return (
// 2. Provider包裹确定上下文生效范围,value注入范围内可用的数据
<MyContext.Provider value={{
money: this.state.money,
updateMoney: this.updateMoney
}}>
<div className="app">
<h1>根组件:{this.state.money}</h1>
<hr />
<Parent />
</div>
</MyContext.Provider>
)
}
}

Parent.jsx

1
2
3
4
5
6
7
8
9
10
11
12
import Child from './Child';
const Parent = () => {
return (
<div className="parent">
<h3>父组件:</h3>
<hr />
<Child />
</div>
);
};

export default Parent;

Child.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { MyContext } from './App'

const Child = () => {
return (
// 3. 通过Consumer来消费数据,value=>{ 这里使用数据 }
<MyContext.Consumer>
{(value) => (
<div className="child">
<h5>子组件:{value.money} <button onClick={()=>value.updateMoney(5000)}>修改money</button></h5>
</div>
)}
</MyContext.Consumer>
);
};

export default Child;

总结:

  • 使用creatContext()创建一个上下文对象,包含:Provider Consumer 组件。
  • 使用 Provider 包裹组件,value 属性注入状态,函数,被包裹组件下的任何组件可以使用。
  • 使用 Consumer 消费 Provider 提供的数据和函数,语法{value=>使用数据和函数}