React-Context

React-Context

一个一个概念打基础中......

今天看到朋友圈里说今天是回文数天,20211202,适合发朋友圈。回文数是经典的编程题目,既然说适合发文章,那我今天也要发一篇。

React继续在看,一个官方不推荐但很多时候又比较有用的特性就是Context上下文,熟悉这个词的朋友都知道,这个就相当于环境的意思。React层层单向数据流动固然好,但如果要接收数据的组件很深,或者有一批组件都需要接受数据,层层传递显得太繁琐,这个时候就用到上下文了。

创建Context对象

创建的方法很简单,使用React.createContext()方法,要传入一个参数作为默认值。这里可以写一个单独的UserContext.js文件:

import React from "react";

const UserContext = React.createContext({user:"minko"});
export default UserContext;

这里传入了一个对象当做初始值,也可以是其他类型的数据。

标明使用Context对象的范围

所谓范围,其实也就是那些组件需要接收这个上下文信息,这就需要用一个特殊的组件,包在这些组件的外层,写法如下:

import React from "react";
import UserContext from "./UserContext";
import Text from "./Text";
import Title from "./Title";

function App() {
    return (
        <UserContext.Provider value={{user:'saner'}}>
            <Text/>
            <Title/>
        </UserContext.Provider>
    );
}

export default App;

这个特殊组件就是上下文对象.Provider,被这个特殊组件包裹的TextTitle组件就是可以接收到上下文中的信息的组件。这个组件特别之处在于那个value属性。

特殊组件的value属性

虽然前边我们传入了默认值,但实际上传递给TextTitle组件上下文中的值,是特殊组件的value属性的值,也就是{user:'saner'}。如果不给value传值,则TextTitle实际拿到的是undefined,这很不好,所以你会看到WebStorm IDE给出的提示是value必须传值。

那么你可能会问,前边为什么要提供默认值,是多此一举吗?答案是,如果没有对应的Provider组件,那么在这个应用里所有使用到了UserContext上下文对象的组件,接收到的就是默认值,这个适合把一个静态的东西在整个应用内广播的场景;一些不会变化的基础设置,就可以通过这样的方法来让所有组件使用。

获取上下文对象中的值

有两种方法来获取上下文对象中的值,分别是使用特殊的Consumer组件和使用this.context

使用Consumer组件

在上边提到的Text组件中,就使用了Consumer组件,写法如下:

import {Component} from 'react';
import UserContext from "./UserContext";

class Text extends Component {

    render() {
        return (
            <UserContext.Consumer>
                {(value)=>(<div>{value.user}</div>)}
            </UserContext.Consumer>
        );
    }
}

export default Text;

这里的核心就是UserContext.Consumer组件,在其内部,提供一个函数并渲染结果,这个函数的参数value(可以叫任意名称)就是上下文中的那个数据,在这个例子中,value就是UserContext.Provider组件中的value属性传入的{user:'saner'}这个对象,所以渲染其user属性,得到saner

这里其实可以把(value)=>(<div>{value.user}</div>)看成是一个组件的代码,外边加上{}实际就是渲染组件,本质上和一个React组件没有任何区别,因为React组件就是函数,渲染就是函数的运行求值结果。

使用this.context

Title组件中使用了this.context,这需要特殊的写法,代码如下:

import {Component} from 'react';
import UserContext from "./UserContext";

class Title extends Component {

    static contextType = UserContext;

    render() {
        return (
            <div>
                {this.context.user}
            </div>
        );
    }
}

export default Title;

其中这一句static contextType = UserContext;是特殊写法,前边的变量名称必须是contextType,采取静态变量的方式,将上下文对象赋给这个变量。

类里边写了这句话之后,在类中,this.context就指向UserContext中的值,也就是UserContext.Provider组件中的value属性传入的{user:'saner'}这个对象,所以渲染之后可以得到saner

两个组件都可以正常获取值,最终渲染的结果是两行saner

不存在Provider的情况

上边的例子看完之后,来看看之前说的,如果找不到对应的Provider会出现什么情况呢,在App中去掉Provider,改成使用Fragment

import React from "react";
import Text from "./Text";
import Title from "./Title";

function App() {
    return (
        <>
            <Text/>
            <Title/>
        </>
    );
}

export default App;

这个时候,就会使用创建UserContext时候的默认值,也就是minko了,最终结果是渲染两行minko

这里的<></>ReactFragment用法,很多时候不想只返回一个元素,而是想返回多个元素,就用<></>进行包裹即可,React在挂载元素内部会把<></>包裹的元素都渲染上。

一个一个知识点攻破吧,虽然React的原理好懂,但是JS本身就很灵活,很多时候写起来不知道代码该如何组织,一方面是因为经验少,另一方面就是React实际上是函数式编程,逻辑抽象的层次比较高。最近又新买了一本不错的书:深入理解React Router:从原理到实践,看到Hook那部分就有点懵了 ,因为作者的抽象层次是比较高的,真的是漫漫长路啊。

女儿寒假的编程班也报好了,到了三年级就准备用Python了,Python作为入门语言还是不错的,写法很像C,可以写的很具体,也可以立刻提高抽象程度,早期就可以来教函数即对象这种思想,柯里化之类也比较好用Python代码来展示,看看著名的CS61A就知道了。如果用Java上来就教人函数式编程,看到诸如

Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;

估计直接就把新人吓跑了。。。。。。

LICENSED UNDER CC BY-NC-SA 4.0
Comment