React多子组件状态管理:父存状态且子可自主修改
React Hook "useState" 无法在回调函数内调用。React Hooks 必须在 React 函数组件或自定义 React Hook 函数中调用
我理解出现该提示的原因,但认为想要实现的功能应该可行,以下是相关代码:
import React, { useState } from "react"; import "./styles.css"; export default function App() { const children = useState([]); return ( <div> {children.map((child, i) => (<Child child={child} />))} <button onClick={() => { children[1]([...children, useState(0)]); }} > Add </button> </div> ); } function Child(props) { const [state, setState] = props.child; return ( <div> <input type="range" min="1" max="255" value={state} onChange={(e) => setState(e.target.value)} ></input> </div> ); }
我希望每个<Child>组件能完全掌控自身状态,无需声明updateChild(i : Index, data)这类函数再去列表中匹配子组件,因为该方式在深层嵌套视图中扩展性差。
请问是否存在方案可同时满足:
- 将状态保留在父组件中(单一数据源)
- 允许子组件修改自身状态
简言之,我想要实现该方案的多子组件版本。
解决方案1:父组件维护状态数组,为子组件生成专属更新函数
这种方式让父组件作为单一数据源,同时每个子组件拿到只属于自己的状态更新方法,无需依赖索引传递,嵌套场景下也能直接向下透传:
import React, { useState, useCallback } from "react"; import "./styles.css"; export default function App() { // 父组件统一管理所有子组件的状态 const [childrenStates, setChildrenStates] = useState([]); // 添加新子组件,追加初始状态0 const addChild = useCallback(() => { setChildrenStates(prev => [...prev, 0]); }, []); // 为指定索引的子组件生成专属更新函数 const createChildUpdater = useCallback((index) => { return (newValue) => { setChildrenStates(prev => { const updatedStates = [...prev]; updatedStates[index] = newValue; return updatedStates; }); }; }, []); return ( <div> {childrenStates.map((state, index) => ( <Child key={index} value={state} updateValue={createChildUpdater(index)} /> ))} <button onClick={addChild}>Add</button> </div> ); } function Child({ value, updateValue }) { return ( <div> <input type="range" min="1" max="255" value={value} onChange={(e) => updateValue(e.target.value)} /> </div> ); }
解决方案2:用useReducer集中管理状态(适合复杂场景)
如果后续需要添加更多状态操作(比如删除子组件),用useReducer可以把所有状态逻辑集中管理,扩展性更强:
import React, { useReducer, useCallback } from "react"; import "./styles.css"; // 定义状态处理逻辑 function childrenReducer(state, action) { switch (action.type) { case "ADD_CHILD": return [...state, 0]; case "UPDATE_CHILD": const newStates = [...state]; newStates[action.index] = action.value; return newStates; // 后续可新增DELETE_CHILD等操作 default: return state; } } export default function App() { const [childrenStates, dispatch] = useReducer(childrenReducer, []); const addChild = useCallback(() => { dispatch({ type: "ADD_CHILD" }); }, []); const updateChild = useCallback((index, value) => { dispatch({ type: "UPDATE_CHILD", index, value }); }, []); return ( <div> {childrenStates.map((state, index) => ( <Child key={index} value={state} updateValue={(val) => updateChild(index, val)} /> ))} <button onClick={addChild}>Add</button> </div> ); } function Child({ value, updateValue }) { return ( <div> <input type="range" min="1" max="255" value={value} onChange={(e) => updateValue(e.target.value)} /> </div> ); }
这两种方案都满足你的需求:父组件作为单一数据源维护所有状态,子组件通过专属的更新方法修改自身状态,不用关心父组件的状态结构或索引匹配问题,深层嵌套时直接把value和updateValue向下传递即可。
内容的提问来源于stack exchange,提问作者frankelot
相关产品推荐
相关产品推荐

