Найдите конкретные дочерние компоненты независимо от глубины реакции

#reactjs

Вопрос:

Я пытаюсь вернуть массив дочерних компонентов, которые соответствуют определенному имени внутри родительского компонента.

Например:

 React.Children.toArray(this.props.children).filter(child => ['TextInput'].includes(child.type.name));
 

Так что, даже если у меня есть:

 <Form>
  <div>
    <TextInput />
  </div>
  <div>
    <div>
      <TextInput />
    </div>
  </div>
</Form>
 

Я мог бы вернуть массив из этих двух TextInput компонентов.

Однако из-за вложенности этих элементов/компонентов фильтр зацикливает только один раз на первой глубине…

Я пытался сделать это рекурсивно, используя flatMap то, что, как я понял, сделает это:

 React.Children.toArray(this.props.children).flatMap(child => ['TextInput'].includes(child.type.name));
 

Однако, похоже, это сработало не так, как я ожидал… есть ли альтернативный способ найти соответствующие компоненты независимо от глубины элемента?

Ответ №1:

Вам понадобится рекурсивная функция:

 function walkAllChildren(root, callback) {
  function walk(e, parents) {
    callback(e, parents);
    const newParents = [...parents, e];
    React.Children.toArray(e.props?.children).forEach((c) => {
      walk(c, newParents);
    });
  }
  walk(root, []);
}

walkAllChildren(
  <main>
    <div>
      <TextInput />
    </div>
    <div>
      <div>
        Hello!
        <TextInput />
      </div>
    </div>
  </main>,
  (e, parents) => {
    if (e.type?.name === "TextInput") {
      console.log(e, parents);
    }
  },
);
 

распечатки

 {type: ƒ TextInput(), key: ".0", ref: null, props: Object, _owner: FiberNode…}
(2) [Object, Object]
{type: ƒ TextInput(), key: ".1", ref: null, props: Object, _owner: FiberNode…}
(3) [Object, Object, Object]
 

(и расширение Object массивов покажет происхождение каждого текстового ввода).

РЕДАКТИРОВАТЬ: то же самое, что и в TypeScript, если вам нужны типы:

 function walkAllChildren(
  root: React.ReactNode,
  callback: (
    element: React.ReactNode,
    parents: readonly React.ReactNode[]
  ) => void
) {
  function walk(element: React.ReactNode, parents: readonly React.ReactNode[]) {
    if (element === null || element === undefined) return;
    callback(element, parents);
    const newParents = [...parents, element];
    const children = (element as any).props?.children;
    React.Children.toArray(children).forEach((child) => {
      walk(child, newParents);
    });
  }
  walk(root, []);
}
 

Комментарии:

1. Спасибо, что нашли элементы. Должен ли я иметь возможность вызывать методы для этих возвращаемых элементов? Я пытался получить к ним доступ, но могу получить доступ только к свойствам, но возвращает функцию, не найденную при попытке вызвать для нее метод.

2. Например, я надеялся, что смогу сделать: this.formFields[0].exampleMethod() что вызовет этот метод для первого дочернего элемента в возвращаемом массиве.

3. Ах, я все понял… пришлось добавить ссылку на TextInput , а затем я могу получить доступ к методу следующим образом: this.formFields[0].ref.current.exampleMethod() . В конечном итоге для доступа к дочерним компонентам React от других требуется довольно много работы 😀