等待来自未知 children 组件编号的值,然后生效

Waiting for values from unknown children component number before effet

假设有一个 parent 分量:

function Parent({ children }) {
  useEffet(() => {
    // Do something when all children 
    // have returned their values via props (or context)
  })
  return <div>{ children }</div>
}

如何才能运行只在所有children都做了某事之后才生效?我不知道 children 的确切数量,它可能会有所不同:

// In this example there are 4 children
function App() {
  return (
    <Parent>
      <Children />
      <Children />
      <Children />
      <div>
        <Children />
      </div>
    </Parent>
  );
}

实际问题是所有 children 都可以准备查询(假设是 ElasticSearch 查询),而 parent 负责实际执行整个查询。

当所有 children 完成后,您可以引入一个 finish 状态 "doing something".

  useEffect(() => {
    const totalChildren = React.Children.count(children);
    if (totalChildren === finish) {
      console.log("All Children Done Something");
      setFinished(0);
    }
  }, [finish]);

对于example

function Children({ num, onClick }) {
  return <button onClick={onClick}>{num}</button>;
}
function Parent({ finish, setFinished, children }) {
  useEffect(() => {
    const totalChildren = React.Children.count(children);
    if (totalChildren === finish) {
      console.log("All Children Done Something");
      setFinished(0);
    }
  }, [finish]);
  return <div>{children}</div>;
}

function App() {
  const [num, setNum] = useState(0);
  const [finish, setFinished] = useState(0);

  const onClick = () => {
    setNum(num + 1);
    setFinished(finish + 1);
  };
  return (
    <Parent finish={finish} setFinished={setFinished}>
      <Children num={num} onClick={onClick} />
      <Children num={num + 1} onClick={onClick} />
      <Children num={num + 2} onClick={onClick} />
      <div>
        <Children num={num + 3} onClick={onClick} />
      </div>
    </Parent>
  );
}

此外,您可以使用 React.cloneElement 并向 children 组件添加 "updating finish state" 功能。

好吧,我们可以将 Children 执行的操作包装到 Promise 中:

  function usePromises() {
    const promises = useMemo([], []);
    const [result, setResult] = useState(undefined);

    useEffect(() => { // this gets deferred after the initialization of all childrens, therefore all promises were created already
          Promise.all(promises).then(setResult);
     }, []);


    function usePromise() {
      return useMemo(() => {
       let resolve;
       promises.push(new Promise(r => resolve = r));
       return resolve;
      }, []);
    }

   return [result, usePromise];
}

现在创建一个共享管理器(在 Parent 内):

  const [queries, useQueryArrived] = usePromises();
  // queries is either undefined or an array of queries, if all queries arrived this component will rerender
  console.log(queries);

然后将useQueryArrived传递给children并在那里使用它(在Children内):

  const queryArrived = useQueryArrived();

  // somewhen:
  queryArrived({ some: "props" });

现在逻辑如下:

Parent 第一次渲染时,会创建 promises 数组。 childrens 将被初始化,并且它们中的每一个都创建一个附加到 promises 的 Promise,Promise 的解析器被记忆化,以便在每次 child 重新渲染时返回相同的解析器。当所有 children 都初始化后,React 渲染并触发效果,这将接受所有承诺并对其调用 Promise.all

现在,当 queryArrived 在 children 中被调用时,promises 解决,当最后一个 promise 解决时,然后调用 setResult 所有的 promises 结果将导致在 Parent 上重新渲染,现在可以使用查询。