【问题标题】:Why is React.forwardRef preventing this HOC from rendering when context updates?为什么 React.forwardRef 在上下文更新时阻止此 HOC 呈现?
【发布时间】:2021-12-30 18:45:54
【问题描述】:

我在处理 this epic react lesson 时遇到了这种行为,并且在重新阅读了 forwardRefHOCs 上的 react 文档后无法弄清楚为什么会发生这种情况。

我已将史诗般的反应课程删减到下方,以展示我所看到的。基本上,它是一个 AppProvider 组件,通过上下文提供对全局状态的访问,然后 withStateSlice HOC 检索正确的状态切片并将其注入到 state 组件的 Cell 属性中。

我看到的奇怪行为是 withStateSlice HOC 在包装组件上调用 forwardRef

return React.memo(React.forwardRef(Wrapper))

分析器显示只有被单击的Cell 实际呈现。

如果我从上面删除forwardRef,那么我们就剩下这个了:

return React.memo(Wrapper)

然后我看到所有 Cell HOC 组件呈现,根据我对上下文的理解,这应该是预期的行为。

从我读到的内容来看,forwardRef 似乎不会在上下文更改时影响渲染,但我可能是错的。这是React 17.0.2,我在开发和生产构建中看到了相同的行为(使用分析打开:react-scripts build --profile

import * as React from 'react'

const AppStateContext = React.createContext()
const AppDispatchContext = React.createContext()

function appReducer(state, action) {
  switch (action.type) {
    case 'UPDATE_CELL': {
      const newState = [...state];
      newState[action.i] = state[action.i] + 1;
      return newState 
    }
    default: {
      throw new Error(`Unhandled action type: ${action.type}`)
    }
  }
}

function AppProvider({children}) {
  const [state, dispatch] = React.useReducer(appReducer, [5, 5, 5, 5, 5, 5])
  return (
    <AppStateContext.Provider value={state}>
      <AppDispatchContext.Provider value={dispatch}>
        {children}
      </AppDispatchContext.Provider>
    </AppStateContext.Provider>
  )
}

function useAppState() {
  const context = React.useContext(AppStateContext)
  if (!context) {
    throw new Error('useAppState must be used within the AppProvider')
  }
  return context
}

function useAppDispatch() {
  const context = React.useContext(AppDispatchContext)
  if (!context) {
    throw new Error('useAppDispatch must be used within the AppProvider')
  }
  return context
}

function fibonacci(n) {
  return n < 1 ? 0
       : n <= 2 ? 1
       : fibonacci(n - 1) + fibonacci(n - 2)
}

function withStateSlice(Comp, slice) {
  const MemoComp = React.memo(Comp)
  function Wrapper(props, ref) {
    const state = useAppState()
    return <MemoComp ref={ref} state={slice(state, props)} {...props} />
  }
  Wrapper.displayName = `withStateSlice(${Comp.displayName || Comp.name})`
  return React.memo(React.forwardRef(Wrapper)) // return React.memo(Wrapper)
}

function Cell({state: cellValue, i}) {
  const dispatch = useAppDispatch()
  const handleClick = () => dispatch({type: 'UPDATE_CELL', i})

  // Artificial slowdown for better visibility in profiler
  for (let n = 10; n < 20; n++) {
    fibonacci(n);
  }

  return (
    <button
      className="cell"
      onClick={handleClick}
    >
      {Math.floor(cellValue)}
    </button>
  )
}
Cell = withStateSlice(Cell, (state, { i }) => state[i])

function App() {
  return (
    <div className="grid-app">
      <div>
        <AppProvider>
          <Cell i={0} />
          <Cell i={1} />
          <Cell i={2} />
          <Cell i={3} />
          <Cell i={4} />
          <Cell i={5} />
        </AppProvider>
      </div>
    </div>
  )
}

export default App

【问题讨论】:

  • 我应该注意到这根本不会改变 App 的行为,从性能的角度来看,使用 React.forwardRef 似乎是需要的,因为只有一个 Cell 与 6 个 Cell 包装器相比.另一个虽然我有可能只是反应分析器的问题,但不确定

标签: reactjs react-hooks react-context


【解决方案1】:

1。问题

当您将 React.forwardRef 放入 React.memo 时,这就像 devtools 中的错误。因此,要知道这些Cells是否真的重新渲染,你可以把console.log()放在Wrapper里面,然后点击Cell

2。关于这个的一些话题

我得到了关于这个话题的一些细节,但仍然没有解决。

【讨论】:

    猜你喜欢
    • 2018-09-16
    • 2019-10-18
    • 2020-11-25
    • 2020-09-14
    • 1970-01-01
    • 2020-02-02
    • 2015-12-18
    • 2012-01-19
    • 2015-08-27
    相关资源
    最近更新 更多