Context 的基本用法
总体而言,使用 Context 分为如下三步:
创建 Context 对象
Context 只能使用 React.createContext 方法创建,代码如下:
// 注释Context中数据的类型 interface IMyContext { lang: string; changeLang: (lang: string) => void } // React.createContext的参数是Context中数据的默认值。 const MyContext = React.createContext<IMyContext >({ lang: 'zh_CN', changeLang: () => { throw Error('你必须自己实现这个方法') } })
用创建的 Context.Provider 包裹组件树。
用 Context.Provider 圈选 Context 的作用域,只有作用域内的组件才能消费 Context 中的数据,此处是管道的入口,在这里放入想要传递的数据。代码如下:
import { MyContext } from './myContext' class ContextDemo extends React.Component<{}> { render() { return ( <MyContext.Provider value={someValue} // 放入数据,它的数据类型必须与IMyContext接口兼容 > // children </GlobalContext.Provider> ) } }
订阅 Context
此处是管道的出口,对于 Context 对象而言,管道入口只有一个,但出口可以有多个。订阅 Context 有多种方式,总体而言有如下三种:
类组件的静态属性 contextType
只能在类组件中使用,并且只能订阅一个 Context。用法如下:
class MyNestedClass extends React.Component<{}> { static contextType = MyContext // 订阅第一步创建的Context render() {/**todo*/} }
在类组件中使用 contextType 订阅 Context 之后,除了不能在构造函数中使用 this.context 访问到数据之外,在类组件的其他位置都能使用 this.context 访问到数据。生命周期 shouldComponentUpdate 的第三个参数是组件即将接收的 context。
useContext
在函数组件中通过 useContext 订阅 Context,useContext 的使用次数不限。用法如下:
function MyNestedFunc() { const myContext = useContext(MyContext) // 订阅第一步创建的Context return (/**todo*/) }
Context.Consumer
它是组件,在 Context 作用域任何位置都能使用它,它只有一个名为 children 的 prop,children 必须是一个返回 React.ReactNode 的函数,该函数接收当前的 context 作为参数,用法如下:
<MyContext.Consumer> {(context) => <MyNestedCom lang={context.lang}/>} </MyContext.Consumer>
Context + useReducer
Context + useReducer 指的是用 useReducer 管理状态,用 Context 传递值。与 Context 基本用法相比需要变化的是第二步,示例代码如下:
// context要传递的数据类型 interface IStyleContext { theme: 'light' | 'dark'; size: 's' | 'm' | 'l'; changeTheme: (theme: 'light' | 'dark') => void; changeSize: (size: 's' | 'm' | 'l') => void; } function ProviderCom() { // 用 useReducer 创建状态 const [style, dispatch] = useReducer(reducer, {theme: 'light', size: 'm'}) // 避免 ContextVal 被频繁新建 const ContextVal = useMemo<IStyleContext>(() => { return { theme: style.theme, size: style.size, changeSize: (size: IStyleContext['size']) => dispatch({type: 'size', payload: size}), changeTheme: (theme: IStyleContext['theme']) => dispatch({type: 'theme', payload: theme}) } }, [style.size, style.theme]) return ( <StyleContext.Provider value={ContextVal}> // 将要传递的数据放到 Context 中 {/* todo */} </StyleContext.Provider> ) }
在上述示例中,状态由 useReducer 创建,被 reducer 函数更新,StyleContext 只负责传递数据,被 StyleContext.Provider 包裹的组件能订阅 StyleContext。
总结
只要 context value 被更新,那么订阅该 context 的组件一定会重新渲染,而不管 context value中更新的那部分值是否被它使用,也不管它的祖先组件是否跳过重新渲染,所以推荐将不同职责的数据保存到不同的 context 中,以减少不必要的重新渲染。
如果 Context.Provider 的 value 属性传递了一个对象字面量,那么 Context.Provider 的父组件每一次重新渲染都会使 context value 更新,进而导致订阅该 context 的组件重新渲染,所有应该避免给 Context.Provider 的 value 传对象字面量。
以上就是React高级概念之Context用法详解的详细内容,更多关于React Context用法的资料请关注好代码网其它相关文章!