0%

React学习之组件之间的通信

在业务中使用React,不可避免地会遇到需要各种组件之间进行通信的情况,这些情况总结起来大概分为以下几种:

  • 父组件向子组件通信
  • 子组件向父组件通信
  • 跨级组件通信
  • 非嵌套组件之间通信

父组件向子组件通信

这是最简单也是最常用的一种组件通信方式,父组件通过props将信息传递给子组件,子组件在接收到props后进行相应的操作。

父组件Parent.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, {useEffect, useState} from 'react'; 
import Child from './Child';
const Parent = () => {
const [message, setMessage] = useState<string>('');
useEffect(() => {
setMessage('我是消息,我会传递给子组件');
},[]);
return (
<div>
<span>我是父组件</span>
<Child message={message}/>
</div>
);
};
export default Parent;

子组件Child.tsx

1
2
3
4
5
6
7
8
9
10
import React from 'react';
const Child = (props: any) => {
const { message } = props;
return (
<div>
<span>{message}</span>
</div>
);
};
export default Child;

在上面例子中,父组件定义了message,并将其作为props传递给子组件,子组件接收props,并将其显示。

子组件向父组件通信

在某些情况中,当子组件的某些值改变,也需要通知父组件做出某些操作或改变。React中遵循单向数据流的原则,不可以直接通过改变props的值来达到改变父组件值的目的,正确的使用方式是:父组件将一个函数作为props传递给子组件,子组件调用该函数来与父组件通信。

父组件Parent.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, {useEffect, useState} from 'react';
import Child from './Child'
const Parent = () => {
const [message, setMessage] = useState<string>('');
useEffect(() => {
setMessage('我是父组件的消息');
},[]);
const changeMessage = () => {
setMessage('我是父组件的消息,我改变啦!');
}
return (
<div>
<span>{message}</span>
<Child changeMessage={changeMessage}/>
</div>
);
};
export default Parent;

子组件Child.tsx

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
const Child = (props: any) => {
const { changeMessage } = props;
return (
<div>
<button onClick={changeMessage}>点我父组件的message会改变噢</button>
</div>
);
};
export default Child;

在上面例子中,父组件定义了changeMessag方法,并将其作为props传递给子组件,子组件接收props,当子组件点击按钮的时候执行props中的changeMessag方法,改变父组件中message的值。

跨级组件通信

所谓跨级组件通信,就是指父组件向子组件的子组件通信,向更深层的子组件通信。跨级组件通信可以采用下面两种方式:

  • 通过props一层一层向下传递
  • 使用context对象

对于第一种方式,如果组件嵌套的层次比较深,中间的每一层组件都要传递props;缺点是显而易见的,增加了程序的复杂度,而且对于中间层组件来说,这些props并不是自己所需要的,增加冗余代码,使代码并不十分友好,所以如果嵌套的层次比较深的话,斟酌使用这种方式。

使用context对象时另一种解决跨组件通信的方式,context相当于一个全局变量,是一个大容器,我们可以把要通信的内容放在这个容器中,这样一来,不管嵌套有多深,都可以随意取用。相对于第一种通信方式,这种方式显得更加友好。

myContext.ts——上下文管理的组件,用来统一导出 Context 实例

1
2
import React from 'react'
export const MyContext = React.createContext({message: '', changeMessage: (value: string) => {}});

父组件Parent.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, {useState, createContext} from 'react';
import Child from './Child'
import { MyContext } from '../context/myContext'
const Parent = () => {
const [message, setMessage] = useState<string>('传给孙子组件');
const changeMessage = (value: string) => {
setMessage(value);
}
return (
<div>
<MyContext.Provider value={{message, changeMessage}}>
<span>我是父组件</span>
<Child />
</MyContext.Provider>
</div>
);
};
export default Parent;

子组件Child.tsx

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import GrandChild from './GrandChild'
const Child = () => {
return (
<div>
<span>我是子组件</span>
<GrandChild />
</div>
);
};
export default Child;

孙子组件GrandChild.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, {useContext} from 'react'
import { MyContext } from '../context/myContext'
const GrandChild = () => {
const {message, changeMessage} = useContext(MyContext);

return (
<div>
我是孙子组件
<button onClick={() => (changeMessage('改变了'))}>
{message}
</button>
</div>
);
};
export default GrandChild;

以上例子中,父组件使用上下文MyContext,并给其赋值message为当前组件的state变量message,changeMessage为当前组件定义的变量changeMessage,用于改变message的值。在孙子组件中,也引入MyContext上下文,并将其作为useContext的参数,从而得到父组件中的message和changeMessage。在孙子组件中点击按钮,直接调用MyContext透传过来的方法,可以修改父组件的message,孙子组件则会重新渲染。这种方式显式的避免了多级 props 的层层透传问题,对于嵌套层次比较深的组件是比较方便的。

非嵌套组件之间通信

非嵌套组件,就是没有任何包含关系的组件,包括兄弟组件以及不在同一个父级中的非兄弟组件。对于非嵌套组件,可以利用二者共同父组件的 context 对象进行通信。