【问题标题】:Using 'Context' and 'useReducer' in a functional component caused all child components to be re-rendered在功能组件中使用 'Context' 和 'useReducer' 会导致所有子组件重新渲染
【发布时间】:2021-05-08 07:41:43
【问题描述】:

React.js 版本:v16.8.2

在功能组件中使用 Context 结合 useReducer,当我更改某些状态时,导致所有由 'Context.Provider' 包裹的子组件被重新渲染。我应该怎么做才能防止对这些子组件进行一些不必要的重新渲染?

现在,我使用 'useMemo' 来包装所有子组件的 DOM 结构。

但是关于这种方式,我想知道它是否有优化性能的效果?因为这样只会导致DOM结构被缓存,但它自己的功能组件还是会被重新执行。

同时我也想知道我使用的'useMemo'缓存的DOM结构是否会再次执行diff算法逻辑?

这是我的代码。

  1. 根组件:
import React, { useReducer } from 'react'
import * as styles from './home.scss'

import { Context } from './context.js'

import Component1 from './component1'
import Component2 from './component2'
import Component3 from './component3'

const Main = () => {
  const initState = {
    count: 0
  }

  const reducer = (state, action) => {
    switch (action.type) {
      case 'add':
        return {
          ...state,
          count: action.payload.count
        }
      default:
        return state
    }
  }

  const [state, dispatch] = useReducer(reducer, initState)

  return (
    <Context.Provider value={{ state, dispatch }}>
      <div className={styles.wrapper}>
        <Component1 />
        <Component2 />
        <Component3 />
      </div>
    </Context.Provider>
  )
}

export default Main
  1. 子组件之一
import React, { useContext, useCallback, useMemo } from 'react'
import * as styles from '../home.scss'

import { Context } from '../context.js'

const Component2 = () => {
  console.log(2)

  const { state, dispatch } = useContext(Context)

  const addClick = useCallback(() => {
    dispatch({
      type: 'add',
      payload: {
        count: 3
      }
    })
  }, [])

  return (
    useMemo(() => {
      return (
        <div className={styles.component2}>
          {console.log(2 + '!')}
          <button onClick={addClick}>add</button>
          <div>{state.count}</div>
        </div>
      )
    }, [addClick, state.count])
  )
}

export default Component2
  1. 上下文
import React from 'react'

export const Context = React.createContext({
  dispatch: () => {},
  state: {}
})

【问题讨论】:

    标签: javascript reactjs diff memo


    【解决方案1】:

    Main 拆分为两个。现在,当 reducer 发生变化时,整个 Main 组件也会重新渲染,包括所有 &lt;ComponentN&gt;。但是,如果你这样做:

    function MyProvider({ children }) {
      const initState = {
        count: 0
      }
    
      const reducer = (state, action) => {
        switch (action.type) {
          case 'add':
            return {
              ...state,
              count: action.payload.count
            }
          default:
            return state
        }
      }
    
      const [state, dispatch] = useReducer(reducer, initState)
    
      const value = React.useMemo(() => ({ state, dispatch }), [state, dispatch])
    
      return (
        <Context.Provider value={value}>
          {children}
        </Context.Provider>
      )
    }
    

    你把Main改成:

    const Main = () => {
      return (
        <MyProvider>
          <div className={styles.wrapper}>
            <Component1 />
            <Component2 />
            <Component3 />
          </div>
        </MyProvider>
      )
    }
    

    那么它仍然具有之前从 Main 获得的相同 children prop,但是 React 在重新渲染时不会重新访问该子树。

    【讨论】:

      猜你喜欢
      • 2022-11-29
      • 2020-02-13
      • 1970-01-01
      • 2020-07-20
      • 2020-03-26
      • 1970-01-01
      • 1970-01-01
      • 2020-01-28
      • 2020-02-15
      相关资源
      最近更新 更多