繁体   English   中英

如何以 React-ful 方式从子组件中检索数据?

[英]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]);

编辑 how-to-retrieve-data-from-children-components-in-a-react-ful-way

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM