React其实提供了一个全局注入变量的API,这就是context api。假如我现在有一个需求是要给我们所有组件传一个文字颜色的配置,我们的颜色配置在最顶级的组件上,当这个颜色改变的时候,下面所有组件都要自动应用这个颜色。那我们可以使用context api注入这个配置:
先使用React.createContext创建一个context // 我们使用一个单独的文件来调用createContext // 因为这个返回值会被Provider和Consumer在不同的地方引用 import React from 'react'; const TestContext = React.createContext(); export default TestContext; 使用Context.Provider包裹根组件创建好了context,如果我们要传递变量给某些组件,我们需要在他们的根组件上加上TestContext.Provider,然后将变量作为value参数传给TestContext.Provider:
import TestContext from './TestContext'; const setting = { color: '#d89151' } ReactDOM.render( <TestContext.Provider value={setting}> <App /> </TestContext.Provider>, document.getElementById('root') ); 使用Context.Consumer接收参数上面我们使用Context.Provider将参数传递进去了,这样被Context.Provider包裹的所有子组件都可以拿到这个变量,只是拿这个变量的时候需要使用Context.Consumer包裹,比如我们前面的Counter组件就可以拿到这个颜色了,只需要将它返回的JSX用Context.Consumer包裹一下就行:
// 注意要引入同一个Context import TestContext from './TestContext'; // ... 中间省略n行代码 ... // 返回的JSX用Context.Consumer包裹起来 // 注意Context.Consumer里面是一个方法,这个方法就可以访问到context参数 // 这里的context也就是前面Provider传进来的setting,我们可以拿到上面的color变量 return ( <TestContext.Consumer> {context => <> <h3 style={{color:context.color}}>Count: {count}</h3> <button onClick={incrementHandler}>计数+1</button> <button onClick={decrementHandler}>计数-1</button> <button onClick={resetHandler}>重置</button> </> } </TestContext.Consumer> );上面代码我们通过context传递了一个全局配置,可以看到我们文字颜色已经变了:
使用useContext接收参数除了上面的Context.Consumer可以用来接收context参数,新版React还有useContext这个hook可以接收context参数,使用起来更简单,比如上面代码可以这样写:
const context = useContext(TestContext); return ( <> <h3 style={{color:context.color}}>Count: {count}</h3> <button onClick={incrementHandler}>计数+1</button> <button onClick={decrementHandler}>计数-1</button> <button onClick={resetHandler}>重置</button> </> );所以我们完全可以用context api来传递redux store,现在我们也可以猜测React-Redux的Provider其实就是包装了Context.Provider,而传递的参数就是redux store,而React-Redux的connectHOC其实就是包装的Context.Consumer或者useContext。我们可以按照这个思路来自己实现下React-Redux了。
手写Provider上面说了Provider用了context api,所以我们要先建一个context文件,导出需要用的context:
// Context.js import React from 'react'; const ReactReduxContext = React.createContext(); export default ReactReduxContext;这个文件很简单,新建一个context再导出就行了,对应的源码看这里。
然后将这个context应用到我们的Provider组件里面:
import React from 'react'; import ReactReduxContext from './Context'; function Provider(props) { const {store, children} = props; // 这是要传递的context const contextValue = { store }; // 返回ReactReduxContext包裹的组件,传入contextValue // 里面的内容就直接是children,我们不动他 return ( <ReactReduxContext.Provider value={contextValue}> {children} </ReactReduxContext.Provider> ) }Provider的组件代码也不难,直接将传进来的store放到context上,然后直接渲染children就行,对应的源码看这里。
手写connect 基本功能