Ожидание значений от неизвестного дочернего номера компонента перед effet - PullRequest
1 голос
/ 22 апреля 2019

Предполагается, что есть родительский компонент:

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

Как запустить эффект только после того, как все дети что-то сделали? Я не знаю точное количество детей, и оно может варьироваться:

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

Фактическая проблема заключается в том, что все дочерние элементы могут подготовить запрос (скажем, ElasticSearch Query), и родительский процесс отвечает за фактическое выполнение всего запроса.

Ответы [ 2 ]

1 голос
/ 23 апреля 2019

Хорошо, мы могли бы обернуть действие, которое Дети выполняют в Обещание:

  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];
}

Теперь создайте одного общего менеджера (внутри Родителя):

  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 для детей и используйте его там (внутри детей):

  const queryArrived = useQueryArrived();

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

Теперь логика выглядит следующим образом:

Родитель будет визуализирован в первый раз, он создастpromises массив.Дети будут инициализированы, и каждый из них создаст Обещание, которое присоединяется к promises распознаватель Обещания запоминается так, что один и тот же распознаватель будет возвращаться при каждом повторном воспроизведении ребенка.Когда все дети были инициализированы, React рендерится и эффект запускается, что примет все обещания и вызовет для них Promise.all.

Теперь когда-нибудь queryArrived вызывается у детей, обещания разрешаются, иногдапоследнее выполненное обещание, которое затем вызывает setResult со всеми результатами обещаний, что приведет к повторному отображению для Родителя, который теперь может использовать запросы.

1 голос
/ 23 апреля 2019

Вы можете ввести состояние finish, когда все дети закончили "что-то делать" .

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

Для пример :

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 и добавить «обновление состояния завершения» к своим дочерним компонентам.

...