![](/img/trans.png)
[英]Proper React-ful way to handle a function in a parent component that will only act on selected child components?
[英]How to retrieve data from children components in a React-ful way?
我有一个带有嵌套组件的大型应用程序。 每个组件都可以单独修改,但我希望能够在会话之间保留数据。 目前,我让每个组件负责使用浏览器 LocalStorage 保存更新,但我最终希望能够将所有应用程序数据导出到单个 JSON 文件,并使用它来保存用户在应用程序中的进度。
在发布时,我最好的想法是使用一个道具来触发传递给所有孩子的回调 function。 当一个道具(我称之为msgPort
被更改)时,每个子组件都会将它们的数据传递给父组件。 到目前为止,它按我的预期工作,但感觉这种方法是不好的做法,而不是“React-ful”。 这种方法是否可以接受,或者将这种方法扩展到更大的应用程序是否存在一些缺陷? 非常感谢任何建议/反馈。
这是一个工作示例: https://codesandbox.io/s/save-nested-data-s5umb?file=/src/App.js
这是来自 CodeSandbox 的相同代码
import "./styles.css";
import { useEffect, useState } from "react";
import "bootstrap/dist/css/bootstrap.css";
function A(props) {
const [data, setData] = useState({
id: props.id,
inputValue: ""
});
useEffect(() => {
if (props.msgPort) {
props.retrieveData(props.order, data);
}
}, [props.msgPort]);
return (
<div className="m-3 text-start p-3 border row g-0">
<div>
<span className="float-start mb-2">Component A</span>
<span className="float-end">ID: {props.id}</span>
</div>
<input
className="form-control"
type="text"
placeholder="inputValue"
value={data.inputValue}
onChange={(evt) => {
setData({ ...data, inputValue: evt.target.value });
}}
/>
</div>
);
}
const B = (props) => {
const [data, setData] = useState({
id: props.id,
checkedValue: true
});
useEffect(() => {
if (props.msgPort) {
props.retrieveData(props.order, data);
}
}, [props.msgPort]);
return (
<form className="m-3 text-start p-3 border row g-0">
<div>
<span className="float-start mb-2">Component B</span>
<span className="float-end">ID: {props.id}</span>
</div>
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
checked={data.checkedValue}
onChange={() => {
setData({ ...data, checkedValue: !data.checkedValue });
}}
/>
<label className="form-check-label">Default checkbox</label>
</div>
</form>
);
};
export default function App() {
const [msgPort, setMsgPort] = useState("");
const [appData, setAppData] = useState([]);
const saveData = () => {
setAppData(["", "", ""]);
setMsgPort("save"); // this will trigger the retrieve data in the children components
};
const retrieveData = (index, componentData) => {
setAppData((prevState) => {
let newData = [...prevState];
newData[index] = componentData;
return newData;
});
setMsgPort(""); // Is there a potential for a component to not get updated before this get reset
};
return (
<div className="App m-2 p-3 border">
<h1 className="h2">Children</h1>
<div className="p-3 m-2 border bg-light">
<A id={1} order={0} msgPort={msgPort} retrieveData={retrieveData} />
<B id={2} order={1} msgPort={msgPort} retrieveData={retrieveData} />
<A id={3} order={2} msgPort={msgPort} retrieveData={retrieveData} />
</div>
<button
type="button"
className="btn btn-light btn-outline-dark"
onClick={() => {
saveData();
}}
>
Get Children Data
</button>
{Object.keys(appData).length > 0 && (
<div className="my-3">
<label className="form-label">Template Data</label>
<span className="form-control-plaintext">
{JSON.stringify(appData)}
</span>
</div>
)}
</div>
);
}
我建议创建一个 React 上下文来保存您想从孩子那里“检索”的 state,只公开一个回调供他们调用并向上传递他们的 state。 这反转了父组件不需要知道其子组件的控制,并且通过使用 React 上下文,您不需要钻取所有 state 和回调作为道具传递给子组件。 这是一个选择加入系统,其中子组件只需要使用useSaveState
挂钩将其 state 发送到集中式上下文值。
例子:
import { createContext, useContext, useEffect, useState } from "react";
const SaveStateContext = createContext({
saveState: () => {}
});
const useSaveState = (key) => {
const { saveState } = useContext(SaveStateContext);
const saveStateWithKey = (value) => saveState(key, value);
return { saveState: saveStateWithKey };
};
const SaveStateProvider = ({ children }) => {
const [state, setState] = useState({});
useEffect(() => {
... any side-effect with the updated states
}, [state]);
const saveState = (key, value) =>
setState((state) => ({
...state,
[key]: value
}));
const value = useMemo(() => ({ saveState }), []);
return (
<SaveStateContext.Provider value={value}>
{children}
</SaveStateContext.Provider>
);
};
用法:
const { saveState } = useSaveState(id);
const [data, setData] = useState( ... );
useEffect(() => {
saveState(data);
}, [data, saveState]);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.