【发布时间】:2021-12-30 18:45:54
【问题描述】:
我在处理 this epic react lesson 时遇到了这种行为,并且在重新阅读了 forwardRef 和 HOCs 上的 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