日期:2024年11月21日

Context

在React中组件间的数据通信是通过props进行的,父组件给子组件设置props,子组件给后代组件设置props,props在组件间自上向下(父传子)的逐层传递数据。但并不是所有的数据都适合这种传递方式,有些数据需要在多个组件中共同使用,如果还通过props一层一层传递,麻烦自不必多说。

Context为我们提供了一种在不同组件间共享数据的方式,它不再拘泥于props刻板的逐层传递,而是在外层组件中统一设置,设置后内层所有的组件都可以访问到Context中所存储的数据。换句话说,Context类似于JS中的全局作用域,可以将一些公共数据设置到一个同一个Context中,使得所有的组件都可以访问到这些数据。

创建Context:

const MyContext = React.createContext(defaultValue);

React.createContext(defaultValue)用来创建一个Context对象,它需要一个初始值作为参数,这个初始值可以是一个原始值,也可以是一个JS对象。调用以后,方法将会返回一个Context对象,这个对象非常关键,当我们想在其他组件中访问Context中的数据时,必须要通过这个对象。

由于Context对象需要在不同的组件中被使用,所以通常我们会将Context对象设置到一个单独的模块中并设置为默认导出像是这样:

import React from "react";

const TestContext = React.createContext({
    name:'孙悟空',
    age:18,
    gender:'男',
    sayHello:()=>{
        alert(this.name);    
    }
});

export default TestContext;

在这个案例中我们暴露的数据比较简单,就是一个简单的JS对象,其中包含了三个属性和一个方法,并最中将产生的Context对象作为默认模块向外部导出。

如果想访问到Context中的数据,我们需要先将Context引入到当前组件中,然后通过Context对象访问其中的数据。

第一种方式,可以通过Consumer标签来访问到Context中的数据:

import React from 'react';
import TestContext from '../store/test-context';

const MyComponent = () => {

    return (
        <TestContext.Consumer>
            {(ctx)=>{
                return (
                    <ul>
                        <li>{ctx.name}</li>
                        <li>{ctx.age}</li>
                        <li>{ctx.gender}</li>
                    </ul>
                );
            }}
        </TestContext.Consumer>

    );
};

export default MyComponent;

访问Context首先我们需要引入之前创建的Context:

import TestContext from '../store/test-context';

Context对象中有一个属性叫做Consumer,直译过来为消费者,如果你了解生产消费者模式这里就比较好理解了,如果没接触过,你可以将Consumer理解为数据的获取工具。你可以将它理解为一个特殊的组件,所以你需要这样使用它:

<TestContext.Consumer>
{(ctx)=>{
return (
<ul>
<li>{ctx.name}</li>
<li>{ctx.age}</li>
<li>{ctx.gender}</li>
</ul>
);
}}
</TestContext.Consumer>

Consumer的标签体必须是一个函数,这个函数会在组件渲染时调用并且将Context中存储的数据作为参数传递进函数,该函数的返回值将会作为组件被最终渲染到页面中。这里我们将参数命名为了ctx,在回调函数中我们就可以通过ctx.xxx访问到Context中的数据。如果需要访问多个Context可以使用多个Consumer嵌套即可。

通过Consumer使用Context实在是不够优雅,所以React还为我们提供了一个钩子函数`useContext()`,我们只需要将Context对象作为参数传递给钩子函数,它就会直接给我们返回Context对象中存储的数据。

import React, {useContext} from 'react';
import TestContext from '../store/test-context';

const MyComponent = () => {

    const ctx = useContext(TestContext);

    return (
        <ul>
            <li>{ctx.name}</li>
            <li>{ctx.age}</li>
            <li>{ctx.gender}</li>
        </ul>
    );
};

export default MyComponent;

像上边那样使用Context并不十分常见,因为这种方式中Context的值是写死的,并不是在组件中指定的。所以React还提供了Provider,用来在组件中指定Context值:

import React from "react";
import MyComponent from "./component/MyComponent";
import TestContext from "./store/test-context";

const App = () => {


    return <TestContext.Provider value={{name:'猪八戒', age:28, gender:'男'}}>
        <MyComponent/>
    </TestContext.Provider>;
};

export default App;

Provider译为生产者,和Consumer消费者对应。Provider会设置在外层组件中,通过value属性来指定Context的值。这个Context值在所有的Provider子组件中都可以访问。Context的搜索流程和JS中函数作用域类似,当我们获取Context时,React会在它的外层查找最近的Provider,然后返回它的Context值。如果没有找到Provider,则会返回Context模块中设置的默认值。

5 3 投票数
文章评分
订阅评论
提醒
guest

6 评论
最旧
最新 最多投票
内联反馈
查看所有评论
Chin
2 年 前

打卡

yutingcoder
yutingcoder
2 年 前

打卡打卡

getooooowen
getooooowen
2 年 前

打卡打卡

Hyzz
Hyzz
2 年 前

超老师问一个问题,
我看您b站上的一个react视频,里面讲到最原始的子孙组件向父组件传递数据的写法,这种写法是不是利用了闭包的原理。 状态是在父组件里,然后在父组件里声明一个函数,最后在这个函数里对父组件的状态进行逻辑处理,最后将这个函数暴露给子孙组件。

闭包2.png
最后由Hyzz编辑于2 年 前
Hyzz
Hyzz
2 年 前

打个卡 ^^

父子组件传参.png
6
0
希望看到您的想法,请您发表评论x