React父组件托管状态时,如何避免子组件间不必要重渲染?
解决React父子组件不必要重渲染的方案(排除React.memo)
方案一:拆分独立状态容器组件
核心思路是将每个表单的状态管理封装到独立容器组件中,容器内部维护自身局部状态,同时将数据同步到父组件的聚合状态。这样每个容器的重渲染仅影响自身,不会波及另一个表单组件。
示例代码:
import React, { useState, useEffect } from "react"; // FormA的独立容器组件,内部管理状态并同步到父组件 const FormAContainer = ({ onDataChange }) => { const [dataA, setDataA] = useState(""); // 将局部状态同步到父组件聚合数据 useEffect(() => { onDataChange(dataA); }, [dataA, onDataChange]); useEffect(() => { console.log("Form A was just rendered"); }); return ( <div> <input onChange={(e) => setDataA(e.target.value)} /> <p>Input Form A: {dataA}</p> </div> ); }; // FormB的独立容器组件 const FormBContainer = ({ onDataChange }) => { const [dataB, setDataB] = useState(""); useEffect(() => { onDataChange(dataB); }, [dataB, onDataChange]); useEffect(() => { console.log("Form B was just rendered"); }); return ( <div> <input onChange={(e) => setDataB(e.target.value)} /> <p>Input Form B: {dataB}</p> </div> ); }; export function BigForm() { const [aggregatedData, setAggregatedData] = useState({ dataA: "", dataB: "" }); // 接收FormA同步的数据 const handleDataAChange = (value) => { setAggregatedData(prev => ({ ...prev, dataA: value })); }; // 接收FormB同步的数据 const handleDataBChange = (value) => { setAggregatedData(prev => ({ ...prev, dataB: value })); }; // 此处可使用聚合数据做后续操作 useEffect(() => { console.log("Aggregated data updated:", aggregatedData); }, [aggregatedData]); return ( <div className="App"> <FormAContainer onDataChange={handleDataAChange} /> <FormBContainer onDataChange={handleDataBChange} /> </div> ); }
方案二:Redux状态管理
通过Redux全局管理表单数据,让每个子组件仅订阅自身需要的状态,触发对应action更新数据,实现精准的重渲染控制。
1. 安装依赖
npm install @reduxjs/toolkit react-redux
2. 创建Redux Store与Slice
// src/store/formSlice.js import { createSlice } from '@reduxjs/toolkit'; const formSlice = createSlice({ name: 'form', initialState: { dataA: '', dataB: '' }, reducers: { updateDataA: (state, action) => { state.dataA = action.payload; }, updateDataB: (state, action) => { state.dataB = action.payload; } } }); export const { updateDataA, updateDataB } = formSlice.actions; export default formSlice.reducer; // src/store/index.js import { configureStore } from '@reduxjs/toolkit'; import formReducer from './formSlice'; export const store = configureStore({ reducer: { form: formReducer } });
3. 配置全局Provider(入口文件)
// src/index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; import { store } from './store'; import { BigForm } from './BigForm'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <BigForm /> </Provider> );
4. 修改表单组件与父组件
// src/BigForm.js import React, { useEffect } from "react"; import { useSelector, useDispatch } from 'react-redux'; import { updateDataA, updateDataB } from './store/formSlice'; const FormA = () => { const dataA = useSelector(state => state.form.dataA); const dispatch = useDispatch(); useEffect(() => { console.log('Form A was just rendered'); }); return ( <div> <input onChange={(e) => dispatch(updateDataA(e.target.value))} /> <p>Input Form A: {dataA}</p> </div> ); }; const FormB = () => { const dataB = useSelector(state => state.form.dataB); const dispatch = useDispatch(); useEffect(() => { console.log('Form B was just rendered'); }); return ( <div> <input onChange={(e) => dispatch(updateDataB(e.target.value))} /> <p>Input Form B: {dataB}</p> </div> ); }; export function BigForm() { // 获取聚合后的表单数据用于后续操作 const aggregatedData = useSelector(state => state.form); useEffect(() => { console.log('Aggregated data updated:', aggregatedData); }, [aggregatedData]); return ( <div className="App"> <FormA /> <FormB /> </div> ); }
内容的提问来源于stack exchange,提问作者Ae Leung
相关产品推荐
相关产品推荐

