React子组件不必要重渲染问题及多子组件Modal优化咨询
解决React组件不必要重渲染及多表单组件优化方案
一、消除ChildB不必要重渲染的具体方案
1. 用React.memo包裹子组件
React默认父组件重渲染时,所有子组件都会跟着重渲染。React.memo会对组件的props进行浅比较,只有当props真正变化时才触发重渲染。
修改ChildB组件:
import { memo } from 'react'; const ChildB = memo((props: { age: number; changeValue: (key: string, value: any) => void; }) => { const { age, changeValue } = props; console.log("==when I change name====, this component re-render"); return ( <InputNumber value={age} onChange={(e) => { changeValue("age", e); }} /> ); });
2. 用useCallback稳定事件处理函数
仅用memo还不够,因为父组件的changeValue函数每次渲染都会重新创建,导致ChildB的props始终变化。需要用useCallback缓存函数,并通过函数式更新避免依赖saveValue:
修改App组件中的changeValue:
import { useCallback } from 'react'; const changeValue = useCallback((key: string, value: any) => { setSaveValue(prevState => ({ ...prevState, [key]: value })); }, []);
此时ChildB只有在age字段变化时才会重渲染,ChildA也可以用同样方式优化。
二、多组件表单的优质设计思路
针对Modal内多子组件、点击确定统一提交的场景,推荐以下三种方案:
1. 受控组件+useReducer优化状态管理
当表单字段较多时,用useReducer替代useState可以更清晰地管理复杂状态更新逻辑,避免父组件代码臃肿:
import { useReducer, useCallback } from 'react'; // 定义状态和操作类型 type FormState = { name: string; age: number }; type FormAction = { type: 'UPDATE_FIELD'; payload: { key: keyof FormState; value: any } }; const formReducer = (state: FormState, action: FormAction): FormState => { switch (action.type) { case 'UPDATE_FIELD': return { ...state, [action.payload.key]: action.payload.value }; default: return state; } }; // 在App组件中使用 const [saveValue, dispatch] = useReducer(formReducer, { name: "wang", age: 18 }); const changeValue = useCallback((key: keyof FormState, value: any) => { dispatch({ type: 'UPDATE_FIELD', payload: { key, value } }); }, []);
2. 非受控组件+useRef收集值
适合表单字段极多、交互复杂的场景,子组件内部管理自身状态,父组件通过ref在提交时统一获取值,彻底避免跨组件重渲染:
import { forwardRef, useImperativeHandle, useState, useRef } from 'react'; // ChildA 非受控实现 const ChildA = forwardRef((_, ref) => { const [name, setName] = useState("wang"); useImperativeHandle(ref, () => ({ getName: () => name })); return <Input value={name} onChange={(e) => setName(e.target.value)} />; }); // ChildB 非受控实现 const ChildB = forwardRef((_, ref) => { const [age, setAge] = useState(18); useImperativeHandle(ref, () => ({ getAge: () => age })); return <InputNumber value={age} onChange={(e) => setAge(e)} />; }); // App组件中收集值 const App: React.FC = () => { const [isModalOpen, setIsModalOpen] = useState(false); const childARef = useRef<{ getName: () => string }>(null); const childBRef = useRef<{ getAge: () => number }>(null); const handleOk = () => { const formData = { name: childARef.current?.getName(), age: childBRef.current?.getAge() }; // 发送至服务器 console.log(formData); setIsModalOpen(false); }; // ...其他逻辑 };
3. 使用专业表单库(如Ant Design Form)
如果项目已使用Ant Design,直接用官方Form组件是最优解——它内置了状态管理、性能优化、校验规则等能力,无需手动处理重渲染:
import { Form, Input, InputNumber, Button, Modal } from 'antd'; const App: React.FC = () => { const [isModalOpen, setIsModalOpen] = useState(false); const [form] = Form.useForm(); const showModal = () => { setIsModalOpen(true); form.setFieldsValue({ name: "wang", age: 18 }); }; const handleOk = () => { form.validateFields().then(values => { // 发送至服务器 console.log(values); setIsModalOpen(false); }); }; return ( <> <Button type="primary" onClick={showModal}>Open Modal</Button> <Modal open={isModalOpen} onOk={handleOk} onCancel={() => setIsModalOpen(false)}> <Form form={form} layout="vertical"> <Form.Item name="name" rules={[{ required: true, message: '请输入姓名' }]}> <Input placeholder="姓名" /> </Form.Item> <Form.Item name="age" rules={[{ required: true, message: '请输入年龄' }]}> <InputNumber placeholder="年龄" /> </Form.Item> </Form> </Modal> </> ); };
内容的提问来源于stack exchange,提问作者sanheart
相关产品推荐
相关产品推荐

