React父组件state更新时如何仅触发子组件重渲染、避免父组件重渲染
React 父组件避免重渲染最优方案
React 组件自身 state 变更时必然触发自身重渲染,这是默认的更新机制,无法绕过。你当前场景下父子组件都重渲染的核心原因是item状态存放在父组件内,调用setItem更新状态时,React 会触发父组件的整树重渲染,子组件自然也会跟着更新。
要实现父组件不重渲染、仅子组件随状态变更更新,核心逻辑是不要将控制子组件渲染的状态存放在父组件中,最优方案根据业务场景分为两种:
方案1:原生无依赖,组件下沉封装(优先推荐)
适用场景:触发状态更新的DOM节点和子组件在结构上可以放在同一层级封装
将状态、触发状态更新的节点、消费状态的Child组件统一封装到一个独立子组件中,原父组件不持有任何相关状态,自然不会触发重渲染。
// 独立封装状态和关联逻辑 const StateContainer = () => { const [item, setItem] = useState(true) return ( <> {/* 注意React没有原生onHover事件,替换为onMouseEnter/onMouseOver */} <div onMouseEnter={() => setItem(!item)}> ... </div> <Child item={item}/> </> ) } // 原父组件不再持有相关状态 const Parent = () => { return ( <div> <StateContainer /> {/* 父组件其他静态内容不会随item变更重渲染 */} </div> ) } const Child = ({item}) => { return item ? ( <div> <h1> Cool! </h1> </div> ) : ( <div> <h1> Not Cool! </h1> </div> ) }
方案2:跨层级场景,使用支持选择性订阅的状态库
适用场景:触发更新的节点和子组件在父组件结构中距离较远,无法封装到同一个子组件内
使用Zustand等支持选择性订阅的轻量状态库,父组件只订阅状态更新函数、不订阅状态值,只有子组件订阅状态本身,状态变更时仅子组件触发重渲染。
import { create } from 'zustand' // 独立存储状态 const useItemStore = create((set) => ({ item: true, toggleItem: () => set((state) => ({ item: !state.item })), })) const Parent = () => { // 仅取更新函数,不订阅状态值,状态变更不会触发父组件重渲染 const toggleItem = useItemStore(state => state.toggleItem) return ( <div> <div onMouseEnter={toggleItem}> ... </div> {/* 其他任意层级的父组件内容 */} <Child /> </div> ) } const Child = () => { // 仅子组件订阅状态值,状态变更仅触发子组件重渲染 const item = useItemStore(state => state.item) return item ? ( <div> <h1> Cool! </h1> </div> ) : ( <div> <h1> Not Cool! </h1> </div> ) }
内容的提问来源于stack exchange,提问作者SteliosK
相关产品推荐
相关产品推荐

