【React】使用React Context在多级组件间传递参数

614 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

本文主要内容:通过一个最简单的例子讲述如何使用context向组件传递参数

这几天忙着做学校里算法与数据结构设计周的项目,只是断断续续地看了一点react context的内容,每次看都觉得有点复杂,直到今天整体地看了一遍也总算捋清了里面的逻辑,心浮气躁的学习果然要不得。

项目预览与分析

image.png

是一个非常简单的例子,写了两个组件<Header /><Button />。我们的目标就是通过react context传递参数,使其能够切换light/dark两种主题。以Hearder.js为例,此时的代码如下

import React, {Component} from "react"

class Header extends Component {
    render() {
        return (
            <header className="light-theme">
                <h2>Light Theme</h2>
            </header>
        )    
    }
}

export default Header

className为light-theme,所以显示的是light的主题,当设置className为dark-theme时,显示的是dark主题,所以我们要改变主题也就是通过参数改变className

这种由App组件直接调用的组件可以非常容易地通过props传递参数,但是如果是像上一篇文章写的有好几级组件,在这种情况下向各个需要的地方传递参数就不那么容易了,所以就会用到React Context。

创建一个Context

为了方便地向各个组件传递参数,我们首先要创建一个React Context,新建一个文件,命名为themeContext.js,然后编写如下代码来创建

import React from "react";
const ThemeContext = React.createContext()
export default ThemeContext

使用这个context

创建后就是使用context,需要知道对于context有两种身份,ProviderConsumer,所以当我们要向App提供参数时,使用如下代码

<ThemeContext.Provider value={"light"}>
    <App />
</ThemeContext.Provider>

contextType

不过在使用Consumer之前,介绍另一种方法,以Header组件为例

import React, {Component} from "react"
import ThemeContext from "./themeContext"

class Header extends Component {
    render() {
        const theme = this.context
        return (
            <header className={`${theme}-theme`}>
                <h2>{theme === "light" ? "Light" : "Dark"} Theme</h2>
            </header>
        )    
    }
}

Header.contextType = ThemeContext

export default Header

首先是引入ThemeContext,然后添加Header.contextType = ThemeContext

这样我们就将传入的参数放到了这个组件的context里,通过this调用

Button同理,再将传入的参数换成dark后,得到如下效果

image.png

Context.Consumer

还是以Header为例,使用consumer需要将组件化为函数组件,所以总体代码如下

import React from "react"
import ThemeContext from "./themeContext"

export default function Header(props) {
    return (
        <ThemeContext.Consumer>
            {theme =>(
                <header className={`${theme}-theme`}>
                    <h2>{theme === "light" ? "Light" : "Dark"} Theme</h2>
                </header>
            )}
        </ThemeContext.Consumer>
    )
}

同样是引入ThemeContext,然后用<ThemeContext.Consumer>包裹要输出的内容,以大括号包裹需要使用context的内容,并且箭头函数前的参数名便是指代context

在context自己的文件中设置参数

虽然说可以通过Context.Provider向App提供参数,但是肯定还是将参数和context集成在同一个文件中更清晰一点,所以为了直接使用context文件,首先要将其转换为一个组件,代码如下

import React, { Component } from "react";
const themeContext = React.createContext()

class ThemeContextProvider extends Component {
    render() {
        return (
            <themeContext.Provider value={"light"}>
                {this.props.children}
            </themeContext.Provider>
        )
    }
}

export default ThemeContextProvider

可以看到此时我们写了一个ThemeContextProvider组件,为了使得后续使用更具有一致性,我们在这里另外提供consumer组件,所以代码如下

import React, { Component } from "react";
const {Provider, Consumer} = React.createContext()

class ThemeContextProvider extends Component {
    render() {
        return (
            <Provider value={"light"}>
                {this.props.children}
            </Provider>
        )
    }
}

export {ThemeContextProvider, Consumer as ThemeContextConsumer}

使用时也是引入后调用即可

将context转化为对象

现在我们已经实现了在同一个文件中对context设置参数,但是目标是希望button组件有着切换主题的功能

所以首先在context文件中写下state和切换state的函数,如下

import React, { Component } from "react";
const {Provider, Consumer} = React.createContext()

class ThemeContextProvider extends Component {
    state = {
        theme: "dark"
    }

    toggleTheme = () => {
        this.setState(prevState => {
            return {
                theme: prevState.theme === "light" ? "dark" : "light"
            }
        })
    }
    
    render() {
        return (
            <Provider value={this.state.theme}>
                {this.props.children}
            </Provider>
        )
    }
}

export {ThemeContextProvider, Consumer as ThemeContextConsumer}

那么接下来的目标就是将这个toggleTheme函数传给Button组件,所以第一步就是输出这个函数,如下

<Provider value={{theme: this.state.theme, toggleTheme: this.toggleTheme}}>
    {this.props.children}
</Provider>

由于此时的参数是个对象,第二步则是要在接收上进行改动,这次以button为例

import React from "react"
import {ThemeContextConsumer} from "./themeContext"

export default function Button(props) {
    return (
        <ThemeContextConsumer>
            {context => (
                <button onClick={context.toggleTheme} className={`${context.theme}-theme`}>Switch Theme</button>
            )}
        </ThemeContextConsumer>
    )    
}

可以看到接收的参数为context,通过.来访问toggleTheme属性和theme属性。实现效果如下

xx2gf-7314u.gif