react之context学习笔记(contextTypes/childContextTypes,createContext,useContext)使用方法,及部分源码翻译

在react16.0之前,如果想要使用context,需要使用childContextTypes以及contextTypes

// 父组件
class TestContext extends Component {
    state = {
        childContext: "123"
    }
    constructor(props) {
        super(props)
    }
    getChildContext() {
        return {
            value: this.state.childContext
        }
    }
    handleChildContextChange = (e) => {
        this.setState({
            childContext: e.target.value
        })
    }
    render() {
        return (
            <div>
                <input type="text" value={this.state.childContext} onChange={this.handleChildContextChange}/>
                {this.props.children}
            </div>
        )
    }
}
// 子组件
class ChildOldApi extends Component {
    render() {
        return (
            <p>childContext: {this.context.value}</p>
        )
    }
}

// 下面这两块是重点,必须要声明一下类型才能够使用
ChildOldApi.contextTypes = {
    value: PropTypes.string
}

TestContext.childContextTypes = {
    value: PropTypes.string,
}

export default () => (
     <TestContext>
         <ChildOldApi />
         <ChildOldApi />
     </TestContext>
)

这可谓是我刚接触react的时候的使用方法了,多年未用,都忘记了。这么做有点繁琐,还要去定义这个contextTypes,每个要用到context的子组件都需要定义一下,且这种context的定义方法对子孙组件的影响太大了,只要有更改,就会让子孙后代全部重新渲染,这就很影响性能。
所以,react团队在16.0之后更新了context的定义方式,用createContext,一下子就简单了许多

const { Provider, Consumer } = createContext(null);

/**
* 这里的Consumer内部是一个方法,接收的就是写在Provider上的value属性
* value可以写对象,也可以直接写值,都是对应的
*/
class ChildNew extends Component {
    render() {
    return <Consumer>{(value) => <p>我是新的:{value.val} {value.a}</p>}</Consumer>;
    }
}
/**
* 这里使用起来就方便多了,直接用Provider标签将内容包起来,里面的所有元素都可以直接通过Consumer获取到
*/
class TestContext extends Component {
    state = {
        childContext: "123",
    };
    constructor(props) {
        super(props);
    }
    handleChildContextChange = (e) => {
        this.setState({
            childContext: e.target.value,
        });
    };
    render() {
        return (
            <Provider value={{
                val: this.state.childContext,
                a: 1
            }}>
                <div>
                    <input type="text" value={this.state.childContext} onChange={this.handleChildContextChange} />
                    <ChildNew />
                </div>
            </Provider>
        );
    }
}

对比一下16.0前后的版本,就会发现,更新后的context确实好用了许多,内部也对性能有了一些优化,这些优化得通过阅读update的源码才能知道他到底优化了哪些地方,这个后面再补吧。而在16.8之后,react HOOKS对context又进行了hooks封装,得到了 useContext 这个hooks方法。

// 这里稍微有点不一样的地方就是 useContext需要接受一个Context对象作为参数,所以这里没有用解构
const ContextHook = createContext(null);
/**
* 这里的使用方法就更加简洁了,直接通过解构的形式,就能获取到之前在Consumer里的接受到的值
*/
const ChildHooks = () => {
    const { val, a } = useContext(ContextHook);
    return (
        <p>
            我是hooks:{val}, a: {a}
        </p>
    );
};
/**
* 所以父节点也要有点调整
*/
class TestContext extends Component {
        // ...
    render() {
        return (
            <ContextHook.Provider
                value={{
                    val: this.state.childContext,
                    a: 1,
                }}
            >
                <input type="text" value={this.state.childContext} onChange={this.handleChildContextChange} />
                <ChildHooks />
            </ContextHook.Provider>
        );
    }
}

hooks就大大简化了代码,而且变得更加易读。
以上就是context近期的一些变化,以及使用方法。源码特别短,主要代码都在更新那一块,还没来得及读,后面理解了源码再来补充这块知识点吧。

// hooks useContext源码
export function useContext<T>(
  Context: ReactContext<T>,
  unstable_observedBits: number | boolean | void,
): T {
  const dispatcher = resolveDispatcher();
  return dispatcher.useContext(Context, unstable_observedBits);
}
// 简化后的createContext的源码

export function createContext<T>(
  defaultValue: T,
  calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext<T> {
  if (calculateChangedBits === undefined) {
    calculateChangedBits = null;
  }


  const context: ReactContext<T> = {
    $$typeof: REACT_CONTEXT_TYPE,
    _calculateChangedBits: calculateChangedBits,
    // As a workaround to support multiple concurrent renderers, we categorize
    // some renderers as primary and others as secondary. We only expect
    // there to be two concurrent renderers at most: React Native (primary) and
    // Fabric (secondary); React DOM (primary) and React ART (secondary).
    // Secondary renderers store their context values on separate fields.
    /**
     * 作为一种支持多个并发渲染器的解决方案,我们将一些渲染器归为主渲染器,另一些则归为次渲染器。
     * 我们最多只期望有两个并发渲染器:React Native(主渲染器)和Fabric(副渲染器);
     * React DOM (primary)和React ART (secondary)。
     * 二级渲染器将它们的上下文值存储在不同的字段中。
    */
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    // Used to track how many concurrent renderers this context currently
    // supports within in a single renderer. Such as parallel server rendering.
    // 用于跟踪此上下文当前在单个呈现程序中支持多少个并发呈现程序。比如并行服务器渲染。
    _threadCount: 0,
    // These are circular(通知)
    Provider: (null: any),
    Consumer: (null: any),
  };


  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };
  
  context.Consumer = context;


  return context;
}

你可能感兴趣的:(笔记,js,react)