以下代码片段的问题是,每当单击更改按钮时,组件
A
和 B
都会被重新渲染,而我只希望组件 C
被重新渲染,因为它是唯一一个消耗上下文的:
const UserContext = React.createContext({user: {name: 'name1'}, change: () => null})
const useUserContext = () => React.useContext(UserContext)
const A = () => {
console.log('rendering A')
return <B/>
}
const B = () => {
console.log('rendering B')
return <C/>
}
const C = () => {
const user = useUserContext()
console.log('rendering C', user)
return (
<div>
<div>UserName:{user.user.name}</div>
<button onClick={user.change}>Change</button>
</div>
)
}
const App = () => {
const [user, setUser] = React.useState({name: 'name2'})
const change = () => {
console.log('changing name')
setUser({name: 'name3'})
}
return (
<div>
<UserContext.Provider value={{user, change}}>
<A/>
</UserContext.Provider>
</div>
)
}
我通过将提供程序逻辑提取到自定义提供程序来完全缓解了该问题:
const UserContext = React.createContext({user: {name: 'name1'}, change: () => null})
const useUserContext = () => React.useContext(UserContext)
const UserProvider = ({children}) => {
const [user, setUser] = React.useState({name: 'name2'})
const change = () => {
console.log('changing name')
setUser({name: 'name3'})
}
return (
<UserContext.Provider value={{user, change}}>
{children}
</UserContext.Provider>
)
}
const A = () => {
console.log('rendering A')
return <B/>
}
const B = () => {
console.log('rendering B')
return <C/>
}
const C = () => {
const user = useUserContext()
console.log('rendering C', user)
return (
<div>
<div>UserName:{user.user.name}</div>
<button onClick={user.change}>Change</button>
</div>
)
}
const App = () => {
return (
<div>
<UserProvider>
<A/>
</UserProvider>
</div>
)
}
但是,我不明白为什么这会对 React 产生任何影响?有人可以解释一下这两种情况的幕后发生了什么吗?
JSX
编译为 React.createElement(...)
并返回一个对象。差异算法会在每次渲染时比较该对象。如果 UserProvider
接受 children
prop 作为组件实例(来自 createElement
的对象),则即使 UserProvider
重新渲染,该对象也不会更改其引用,因为它不是在 UserProvider
内部创建,而是在其父级中创建。
在第一个示例中,每次渲染都会调用
React.createElement(A)
。在你的第二个例子中 - 仅在 App
中出现一次