React Context

12 5月

React 15版的context写法:

注册context:

import React, { Component } from 'react';

export default class ThemeProvider extends Component {
    static childContextTypes = {
        theme: PropTypes.string,
    };

    getChildContext() {
        return {
            theme: this.props.theme,
        };
    }
    ...
}

消费context:

import React, { Component } from 'react';

export default class ThemeButton extends Component {
    static contextTypes = {
        theme: PropTypes.string,
    }
    ...
    render() {
        return <button theme={this.props.theme}>点击</button>;
    }
}

React 16开始context的写法有点不同。

React.createContext(defaultValue)创建一个Context对象。当React渲染订阅该Context对象的组件时,会从组件树中最近的Provider中读取值。

<MyContext.Provider value={…}>组件,提供context。value属性绑定context中的值,当value值发生变化时,consumer都将重新渲染,而且不受shouldComponentUpdate的约束。一个Provider可以连接多个consumer。

<MyContext.Consumer>{ value => … }</MyContext.Consumer>组件,订阅context。接受一个函数,参数value是最近Provider提供的context值,返回一个React组件。

Class.contextType订阅单个context,它是类组件的static属性,指定后类中就可以通过this.context来获取值。

context里就一个值时的板式代码:

// 注册 context
export const ThemeContext = React.createContext(themes.dark);

// 提供 context
<ThemeContext.Provider value={this.state.theme}>。。。</ThemeContext.Provider>

// 消费 context
const theme = this.context;

详细如下,注册context:

import React from 'react';

export const themes = {
    light: {
        foreground: '#000000',
        background: '#eeeeee',
    },
    dark: {
        foreground: '#ffffff',
        background: '#aaaaaa',
    },
};

export const ThemeContext = React.createContext(themes.dark);

提供context:

import React, { Component } from 'react';
import { themes, ThemeContext } from './theme-context';
import ThemeButton from './theme-button';

export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            theme: themes.light,
        };
    }
    
    toggleTheme = () => {
        this.setState(state => ({
            theme: (state.theme === themes.dark ? themes.light : themes.dark),
        }));
    };

    render() {
        return (
            <ThemeContext.Provider value={this.state.theme}>
                <ThemeButton onClick={this.toggleTheme}>改变主题</ThemeButton>
            </ThemeContext.Provider>
        );
    }
}

消费context:

import React, { Component } from 'react';
import { ThemeContext } from './theme-context';

export default class ThemedButton extends Component {
    static contextType = ThemeContext;  // 订阅单个context,可以通过this.context来获取值

    render() {
        const theme = this.context;
        return <button {...this.props} style={{ backgroundColor: theme.background }} />;
    }
}

可以给context提供一个函数,让子组件更新context的值,板式代码:

// 注册 context
export const ThemeToggleContext = React.createContext({
    theme: themes.dark,
    toggleTheme: () => {}
});

// 提供 context
<ThemeToggleContext.Provider value={this.state}>。。。</ThemeToggleContext.Provider>

// 消费 context
<ThemeToggleContext.Consumer>
{
    ({theme, toggleTheme}) => 。。。
}
</ThemeToggleContext.Consumer>

详细代码如下,注册context:

export const ThemeToggleContext = React.createContext({
    theme: themes.dark,
    toggleTheme: () => {},
});

提供context:

import React, { Component } from 'react';
import { themes, ThemeToggleContext } from './theme-context';
import ThemeToggleButton from './theme-toggle-button';

export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            theme: themes.light,
            toggleTheme: this.toggleTheme,
        };
    }
    
    toggleTheme = () => {
        this.setState(state => ({
            theme: (state.theme === themes.dark ? themes.light : themes.dark),
        }));
    };

    render() {
        return (
            <ThemeToggleContext.Provider value={this.state}>
                <ThemeToggleButton />
            </ThemeToggleContext.Provider>
        );
    }
}

消费context:

import React, { Component } from 'react';
import { ThemeToggleContext } from './theme-context';

export default class ThemedButton extends Component {
    render() {
        return (
            <ThemeToggleContext.Consumer>
                {
                    ({theme, toggleTheme}) => (
                        <button onClick={toggleTheme} style={{backgroundColor: theme.background}}>
                            改变主题2
                        </button>)
                }
            </ThemeToggleContext.Consumer>
        );
    }
}

上面例子中,Provider的value最好和state绑定。不要直接绑定对象,否则每次Provider重新渲染时,value都会创建一个新对象。导致重新渲染所有Consumer,这是不必要的性能浪费。

不好的写法:

<Provider value={{something: 'something'}}>

好的写法:

<Provider value={this.state.value}>

发表评论

电子邮件地址不会被公开。 必填项已用*标注