美文网首页
03-react-跨组件级通信Context

03-react-跨组件级通信Context

作者: 低头看云 | 来源:发表于2020-10-13 13:48 被阅读0次

    react-跨组件级通信- Context

    之前编写组件都是通过props或者state的方式来传递组件, 但组件层级多起来时候,就会出现多次从顶层组件一直传递到最底层的组件中使用。

    image-20201013092822013

    Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

    image-20201013093720954

    Context常用API

    React.createContext

    创建一个 Context 对象。

    const DemoContext = React.createContext(defaultValue);
    // defaultValue 默认值
    

    Context.Provider

    Provider 接收一个 value属性,传递给消费组件。

    <DemoContext.Provider value={/* 某个值 */}>
    

    当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。

    Context.Consumer

    <DemoContext.Consumer>
      {value => /* 基于 context 值进行渲染*/}
    </DemoContext.Consumer>
    

    这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。传递给函数的 value 值等等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext()defaultValue

    基本使用

    基本使用context

    import React, { useState } from 'react'
    // 创建一个context对象, 并设置默认值为 测试
    const DemoContext = React.createContext('测试')
    
    function ContextPage() {
    
      return (
          <DemoContext.Consumer>
            {(ctx) => {
              console.log('ctx', ctx)  // 测试
              return <div>{ctx}</div>
            }}
          </DemoContext.Consumer>
      )
    }
    
    export default ContextPage
    
    

    注意点

    只有组件Consumer没有匹配到Provider时, CreateContext设置的默认值("测试")才会生效. 这有助于在不使用 Provider 包装组件的情况下对组件进行测试。

    undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效

     import React, { useState } from 'react'
    // 创建一个context对象, 并设置默认值为 测试
    const DemoContext = React.createContext('测试')
    
    function ContextPage() {
    
      return (
         <DemoContext.Provider>
            <DemoContext.Consumer>
              {(ctx) => {
                console.log('ctx', ctx) // 此时不生效 undefined
                return <div>{ctx}</div>
              }}
            </DemoContext.Consumer>
          </DemoContext.Provider>
      )
    }
    
    export default ContextPage
    
    

    切换主题颜色案例

    1.创建context对象

    // context.js
    
    import React from 'react'
    
    export const ThemeContext = React.createContext({ themeColor: 'pink' })
    
    export const ThemeProvider = ThemeContext.Provider
    
    export const ThemeConsumer = ThemeContext.Consumer
    

    2.创建提供商 provider

    // contextPage.js
    import React, { useState } from 'react'
    import { ThemeProvider } from './context'
    import ConsumerPage from './ConsumerPage'
    import UseContextPage from './UseContextPage'
    
    function ContextPage() {
      const [theme, setTheme] = useState({ themeColor: 'red' })
      return (
        <ThemeProvider value={theme}>
          <button
            onClick={() => {
              theme.themeColor == 'red'
                ? setTheme({ themeColor: 'green' })
                : setTheme({ themeColor: 'red' })
            }}
          >
            切换主题
          </button>
          
          {/* 基本使用 */}
          <ConsumerPage />
          {/* hooks写法 */}
          <UseContextPage />
        </ThemeProvider>
      )
    }
    
    export default ContextPage
    
    

    3.创建消费者 consume

    import React, { Component } from 'react'
    import { ThemeConsumer, ThemeContext } from './context'
    
    export default class ConsumerPage extends Component {
        // 可以使用 static 这个类属性来初始化你的 contextType。
      // static contextType = ThemeContext
      render() {
        let value = this.context
        console.log('value', value)
    
        return (
          <div>
            <h3>ConsumerPage</h3>
            <ThemeConsumer>
              {(themeContext) => {
                console.log('themeContext', themeContext)
                return <div className={themeContext.themeColor}>我是内容</div>
              }}
            </ThemeConsumer>
          </div>
        )
      }
    }
    
    //挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。
    ConsumerPage.contextType = ThemeContext
    
    

    注意点:

    • 可以通过挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。
    • 可以使用 static 这个类属性来初始化你的 contextType,拿到Context对象
    • 可以使用ThemeConsumer的render-prop 拿到Contxt对象

    4.Hook中useContext写法

    const {themeColor} = useContext(ThemeContext);
    

    接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。useContext 的参数必须是 context 对象本身

    import React, { useContext } from 'react'
    import { ThemeContext } from './context'
    
    export default function UseContextPage(props) {
      // 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。
      // 当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。
    
      /**
       别忘记 useContext 的参数必须是 context 对象本身:
    
        正确: useContext(MyContext)
        错误: useContext(MyContext.Consumer)
        错误: useContext(MyContext.Provider)
       */
      const { themeColor } = useContext(ThemeContext)
      return (
        <div className="border">
          <h3 className={themeColor}>UseContextPage</h3>
        </div>
      )
    }
    

    当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。

    即使祖先使用 React.memoshouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。

    • 效果
    image

    useContext的几个优化方案

    1.将组件一分为二,放在memo两者之间

    • 可以通过将组件拆分为两个并将更具体的道具传递给内部的道具来优化渲染。
    import React, { memo, useContext, useMemo } from 'react'
    import { ThemeContext } from './context'
    
    export default function UseContextPage(props) {
      
      const { themeColor } = useContext(ThemeContext)
    
      return (
    
        // 处理其他的渲染逻辑
    
        <Two themeColor={themeColor} />
      )
    
      // return useMemo(() => <Three className={themeColor} />, [themeColor])
    }
    
    
    const Two = memo(({ themeColor }) => {
      return (
        <div className={themeColor}>
          3232
          <h3>UseContextPage</h3>
        </div>
      )
    })
    

    2.一个组件useMemo内部

    • 可以通过将返回值包装在其中useMemo并指定其依赖项来将其保留在单个组件中。我们的组件仍然可以重新执行,但是如果所有useMemo输入都相同,React不会重新渲染子树。
    import React, { memo, useContext, useMemo } from 'react'
    import { ThemeContext } from './context'
    
    export default function UseContextPage(props) {
      
      const { themeColor } = useContext(ThemeContext)
    
     return useMemo(() => <Three className={themeColor} />, [themeColor])
    }
    
    
    function Three(props) {
      return (
        <div className={props.className}>
          3232
          <h3>UseContextPage</h3>
        </div>
      )
    }
    

    相关文章

      网友评论

          本文标题:03-react-跨组件级通信Context

          本文链接:https://www.haomeiwen.com/subject/nkjvpktx.html