【问题标题】:Communication Parent and Child component with useEffect使用 useEffect 通信父子组件
【发布时间】:2023-03-06 19:09:01
【问题描述】:

我遇到了父子组件之间的通信问题。

我希望父母(主机)保持自己的状态。我希望孩子(来宾)通过该状态并对其进行修改。孩子有他的本地版本的状态,可以改变孩子想要的任何东西。然而,一旦孩子玩完状态,他就会将它传递给父母以实际“保存”实际状态。 我将如何正确实现这一点?

我的代码中的问题:

  • 在 updateGlobalData 处理程序上,我同时记录了 data 和 newDataFromGuest,它们是相同的。我希望 data 代表旧版本的数据,newDataFromGuest 代表新的

  • updateGlobalData 被称为 2X。我可以通过从 useEffect 内部的 deps 数组中删除 updateGlobalData ref 来解决这个问题,但我不想搞砸它。

我想要的结果应该是:

  • 数据状态应该保持旧数据,直到调用 updateGlobalData
  • 我希望 updateGlobalData 在我单击按钮时只触发一次

来自 Codesandbox 的代码:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const Host = () => {
  const [data, setData] = useState({ foo: { bar: 1 } });

  const updateGlobalData = newDataFromGuest => {
    console.log(data);
    console.log(newDataFromGuest);

    setData(newDataFromGuest);
  };

  return <Guest data={data} updateGlobalData={updateGlobalData} />;
};

const Guest = ({ data, updateGlobalData }) => {
  const [localData, setLocalData] = useState(data);
  const changeLocalData = newBarNumber => {
    localData.foo = { bar: newBarNumber };
    setLocalData({ ...localData });
  };

  useEffect(() => {
    updateGlobalData(localData);
  }, [localData, updateGlobalData]);

  return (
    <div>
      <span>{localData.foo.bar}</span> <br />
      <button onClick={() => changeLocalData(++localData.foo.bar)}>
        Increment
      </button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<Host />, rootElement);

【问题讨论】:

  • RonH 尽管您的问题已被搁置,但我可能会为您解答。请按照上面的要求编辑问题,我将提供解决方案。
  • 我做到了。希望它很快就会停止,您可以与我分享解决方案
  • @ravibagul91 你能否取消这个问题的“等待”状态?答案已被编辑。提前致谢!

标签: reactjs react-hooks use-effect


【解决方案1】:

注意:下面的代码解决方案

  • 问题 1:

我希望在单击按钮时只触发一次 updateGlobalData

为了解决这个问题,我混合使用了React.createContext 和钩子useReducer。这个想法是通过其上下文使主机调度程序可用。这样,您不需要将“updateGlobalData”回调发送给 Guest,也不需要使 useEffect 挂钩依赖于它。因此,useEffect 只会被触发一次。

但请注意,useEffect 现在依赖于主机 dipatcher,您需要将其包含在其依赖项中。不过,如果您阅读useReducer 上的第一个注释,调度程序是稳定的,不会导致重新渲染。

  • 问题 2:

数据状态应该保持旧数据,直到调用 updateGlobalData

解决方案很简单:不要直接更改状态数据!!请记住,Javascript 中的大多数值都是通过引用传递的。如果您将数据发送给访客并直接修改它,就像这里

const changeLocalData = newBarNumber => {
  localData.foo = { bar: newBarNumber }; // YOU ARE MODIFYING STATE DIRECTLY!!!
  ...
};

这里

<button onClick={() => changeLocalData(++localData.foo.bar)}> // ++ OPERATOR MODIFYES STATE DIRECLTY

它们也将在主机中被修改,除非您通过 useState 挂钩更改该数据。我认为(不是 100% 肯定)这是因为来宾中的 localData 使用与来自主机的数据相同的引用进行初始化。因此,如果您直接在 Guest 中更改它,它也会在 Host 中更改。只需将本地数据的值加 1 即可更新访客状态,而无需使用 ++ 运算符。像这样:

localData.foo.bar + 1

这是my solution

import React, { useState, useEffect, useReducer, useContext } from "react";
import ReactDOM from "react-dom";

const HostContext = React.createContext(null);

function hostReducer(state, action) {
  switch (action.type) {
    case "setState":
      console.log("previous Host data value", state);
      console.log("new Host data value", action.payload);
      return action.payload;
    default:
      throw new Error();
  }
}

const Host = () => {
  // const [data, setData] = useState({ foo: { bar: 1 } });
  // Note: `dispatch` won't change between re-renders
  const [data, dispatch] = useReducer(hostReducer, { foo: { bar: 1 } });

  // const updateGlobalData = newDataFromGuest => {
  //   console.log(data.foo.bar);
  //   console.log(newDataFromGuest.foo.bar);

  //   setData(newDataFromGuest);
  // };

  return (
    <HostContext.Provider value={dispatch}>
      <Guest data={data} /*updateGlobalData={updateGlobalData}*/ />
    </HostContext.Provider>
  );
};

const Guest = ({ data /*, updateGlobalData*/ }) => {
  // If we want to perform an action, we can get dispatch from context.
  const hostDispatch = useContext(HostContext);
  const [localData, setLocalData] = useState(data);
  const changeLocalData = newBarNumber => {
    // localData.foo = { bar: newBarNumber };
    // setLocalData({ ...localData });
    setLocalData({ foo: { bar: newBarNumber } });
  };

  useEffect(() => {
    console.log("useEffect", localData);
    hostDispatch({ type: "setState", payload: localData });
    // updateGlobalData(localData);
  }, [localData, hostDispatch /*, updateGlobalData*/]);

  return (
    <div>
      <span>{localData.foo.bar}</span> <br />
      <button onClick={() => changeLocalData(localData.foo.bar + 1)}>
        Increment
      </button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<Host />, rootElement);

如果您发现任何与您想要的不符的地方,请告诉我,我会重新检查。

希望对你有帮助。

最好, 最大。

【讨论】:

  • 嘿,麦克斯。是的,最终我得出了与你为我的两个问题所写的相同的结论。但是,您确实投入了很多工作,并且您的解决方案确实解决了我的问题,因此我将其标记为正确。谢谢你的时间,我很感激!
  • @RonH 很高兴它对您有所帮助。如果您需要更多答案,可以联系我。只需@我的名字即可获得通知。感谢投票!
猜你喜欢
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
  • 2021-02-06
  • 2021-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多