TypeScript и React - детский тип?

У меня есть очень простой функциональный компонент:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


const aux = (props: AuxProps) => props.children;

export default aux;

И еще один компонент:

import * as React from "react";

export interface LayoutProps  { 
   children: React.ReactNode
}

const layout = (props: LayoutProps) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {props.children}
        </main>
    <Aux/>
);

export default layout;

Я получаю следующую ошибку:

[ts] JSX element type 'ReactNode' is not a constructor function for JSX elements. Type 'undefined' is not assignable to type 'ElementClass'. [2605]

Как мне это правильно набрать?

Чтобы не изобретать колесо заново, вы также можете использовать React.FC для определения своего функционального компонента. то есть, const layout React.FC = (props) => {/*component body*/}

Kashif Nazar 27.05.2021 00:21
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
417
1
277 252
18

Ответы 18

Компоненты React должны иметь один узел-оболочку или возвращать массив узлов.

Компонент <Aux>...</Aux> имеет два узла: div и main.

Попробуйте обернуть своих детей компонентом div в Aux.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

const aux = (props: AuxProps) => (<div>{props.children}</div>);

export default aux;

Не правда. В React 16+ это уже не так, плюс ошибка говорила бы об этом, если бы это было так

Andrew Li 09.12.2018 04:05

Чтобы использовать <Aux> в вашем JSX, это должна быть функция, возвращающая ReactElement<any> | null. Это определение функциональная составляющая.

Однако в настоящее время он определен как функция, возвращающая React.ReactNode, который является гораздо более широким типом. Как говорят типы React:

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

Убедитесь, что нежелательные типы нейтрализованы, заключив возвращаемое значение в React Fragment (<></>):

const aux: React.FC<AuxProps> = props =>
  <>{props.children}</>;

Я не понимаю, зачем нужно оборачивать children в React Fragment. Не хочешь объяснить? В React вполне допустимо использовать только return children;.

sanfilippopablo 25.07.2019 03:48

Нет, если children - это массив. В этом случае вам нужно либо обернуть их в один узел, либо использовать React Fragments.

Karol Majewski 25.07.2019 11:48

Да, у меня в коде есть: return React.Children.count(children) > 1 ? <>{children}</> : children, но машинописный текст на это жалуется. Заменил просто на <>{children}</>.

sanfilippopablo 25.07.2019 16:38

Он жалуется, потому что children - это список элементов, который содержит только 1 элемент. Думаю, сработало бы, если бы вы сделали return React.Children.count(children) > 1 ? <>{children}</> : children[0] @sanfilippopablo

tamj0rd2 17.08.2019 21:27

Похоже, это не работает в более новых версиях React. Я вошел в консоль и могу подтвердить, что у меня есть детская опора, но даже с <React.Fragment>, окружающим {props.children}, я ничего не возвращаю.

Mark 29.02.2020 01:09

Как насчет того, когда children - это функция?

t3__rry 25.06.2020 11:16

Обертывание во Фрагмент <> </> - вот в чем дело! Спасибо)

Igor 27.10.2021 17:27

Вот что сработало для меня:

interface Props {
  children: JSX.Element[] | JSX.Element
}

Редактировать Я бы порекомендовал использовать вместо этого children: React.ReactNode.

Это недостаточно широкое определение. Ребенок мог быть строкой.

Mike S 26.04.2019 01:09

По крайней мере, этот пример сработал для меня, поскольку мой компонент должен был ожидать 1 или несколько JSX.Elements. Спасибо!

Italo Borges 28.05.2019 17:07

Это не будет работать с выражениями JavaScript в качестве дочерних <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. Использование children: ReactNode будет работать.

Nickofthyme 05.10.2019 19:56

или используйте PropsWithChildren, как упоминали другие. Я лично предпочитаю его children: React.ReactNode

kimbaudi 10.02.2021 20:17

С сайта TypeScript: https://github.com/Microsoft/TypeScript/issues/6471

The recommended practice is to write the props type as {children?: any}

У меня это сработало. Дочерним узлом может быть много разных вещей, поэтому явная типизация может пропустить случаи.

Здесь более подробно обсуждается следующая проблема: https://github.com/Microsoft/TypeScript/issues/13618, но любой подход по-прежнему работает.

Просто children: React.ReactNode.

Это правильный ответ! JSX.Element недостаточно хорош, так как допустимые дочерние элементы React могут быть строкой, логическим значением, нулем ... ReactChild тоже неполный по тем же причинам

Pierre Ferry 27.01.2020 16:56

@PierreFerry Проблема в том, что почти что-либо можно назначить ReactNode. Это не помогает с точки зрения безопасности типов, подобно тому, как вводить children как any - см. Мой отвечать для объяснения.

ford04 17.03.2020 14:29

Спасибо за ответ @ ford04. В моем случае я хочу, чтобы дети имели свободный тип. Мой компонент принимает любые допустимые дочерние элементы React. Так что я считаю, что это все еще тот ответ, который я искал :)

Pierre Ferry 24.03.2020 15:19

или PropsWithChildren

kimbaudi 10.02.2021 20:19

В замешательстве ... разве это не то, что у OP уже есть?

Janac Meena 26.06.2021 15:33

вы можете объявить свой компонент следующим образом:

const MyComponent: React.FunctionComponent = (props) => {
    return props.children;
}

сегодня общее мнение таково, что React.FunctionComponent (или сокращенно React.FC) - это обескураженный. В вашем случае я бы исключил использование React.FC и просто сделал const MyComponent = ({children}: PropsWithChildren<{}>)

kimbaudi 10.02.2021 20:18

Общее способ найти любой тип на собственном примере. Прелесть машинописного текста в том, что у вас есть доступ к типам все, если у вас есть правильные файлы @types/.

Чтобы ответить на этот вопрос, я просто подумал об использовании компонента React, который имеет опору children. Первое, что пришло в голову? Как насчет <div />?

Все, что вам нужно сделать, это открыть vscode и создать новый файл .tsx в проекте реакции с @types/react.

import React from 'react';

export default () => (
  <div children = {'test'} />
);

При наведении указателя мыши на опору children отображается тип. А что вы знаете - его тип - ReactNodeReactNode[] нет необходимости).

Затем, если вы щелкните определение типа, вы сразу перейдете к определению children, полученному из интерфейса DOMAttributes.

// node_modules/@types/react/index.d.ts
interface DOMAttributes<T> {
  children?: ReactNode;
  ...
}

Note: This process should be used to find any unknown type! All of them are there just waiting for you to find them :)

Ctrl + щелчок (в окнах) приведет вас к указанному определению типа. Может быть всем известно, но мне пришлось поискать. Спасибо, что научили нас ловить рыбу, а не просто давать рыбу, кстати.

gcr 16.02.2021 03:56

В качестве типа, содержащего дочерние элементы, я использую:

type ChildrenContainer = Pick<JSX.IntrinsicElements["div"], "children">

Этот тип дочернего контейнера является достаточно общим, чтобы поддерживать все различные случаи, а также согласован с API ReactJS.

Итак, для вашего примера это будет примерно так:

const layout = ({ children }: ChildrenContainer) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {children}
        </main>
    <Aux/>
)

Тип возвращаемого значения функционального компонента ограничен JSXElement | null в TypeScript. Это текущее ограничение типа, возвращаемые типы чистого React позволяет больше.

Минимальный демонстрационный фрагмент

Вы можете использовать утверждение типа или фрагменты в качестве обходного пути:

const Aux = (props: AuxProps) => <>props.children</>; 
const Aux2 = (props: AuxProps) => props.children as ReactElement; 

ReactNode

children: React.ReactNode может быть неоптимальным, если цель состоит в том, чтобы иметь сильные типы для Aux.

Практически все может быть отнесено к текущему типу ReactNode, который эквивалентен {} | undefined | null. Более безопасным вариантом для вашего случая может быть:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Пример:

Учитывая, что Aux нуждается в элементах React как children, мы случайно добавили к нему string. Тогда выше решение будет ошибкой в ​​отличие от ReactNode - взгляните на связанные игровые площадки.

Типизированный children также полезен для свойств, отличных от JSX, таких как обратный вызов Рендеринг опоры.

Эти ответы кажутся устаревшими - React теперь имеет встроенный тип PropsWithChildren<{}>. Он определяется аналогично некоторым правильным ответам на этой странице:

type PropsWithChildren<P> = P & { children?: ReactNode };

Что такое P в случае этого типа?

sunpietro 08.07.2020 10:42
P - это тип реквизита вашего компонента
Tim Iles 17.07.2020 16:16

Да! это прекрасно сработало. interface LayoutProps {} const Layout = ({children}: PropsWithChildren<LayoutProps>) => {...}

cyrf 12.12.2020 19:17

Вы можете использовать ReactChildren и ReactChild:

import React, { ReactChildren, ReactChild } from 'react';
 
interface AuxProps {
  children: ReactChild | ReactChildren;
}

const Aux = ({ children }: AuxProps) => (<div>{children}</div>);

export default Aux;

Если вам нужно передать плоские массивы элементов:

interface AuxProps {
  children: ReactChild | ReactChild[] | ReactChildren | ReactChildren[];
}

Для всех, кто оказался здесь, это неправильно. ReactChildren не равен ReactChild[], так как это тип служебной функции. reactjs.org/docs/react-api.html#reactchildren. если вы используете ReactChild | ReactChildren, вы не сможете передать потомков в виде массива.

kay 07.01.2021 08:24

@kay Спасибо за это. Я добавил к ответу определение плоских массивов. Он должен охватывать и ваш случай. Дай мне знать.

Wilk 07.01.2021 16:26

Спасибо! Однако я не думаю, что ReactChildren здесь подходящий. Думаю хватит children: ReactChild | ReactChild[] или просто ReactNode

kay 08.01.2021 02:54

Я не согласен. Лучше создать тип / интерфейс как ReactNode по сравнению с ReactChild и ReactChildren, поскольку он принимает больше, чем просто ReactChild / ReactChildren. Или, что еще лучше, используйте PropsWithChildren, как уже упоминали другие.

kimbaudi 10.02.2021 20:12

Вы также можете использовать React.PropsWithChildren<P>.

type ComponentWithChildProps = React.PropsWithChildren<{example?: string}>;

Это именно то, чем я занимаюсь, и я думаю, что это более чистый способ использовать правильный набор текста.

Gustavo Straube 03.09.2020 17:02

Мне нравится это решение, потому что оно расширяемое. type AuthProviderProps = React.PropsWithChildren<{}> позволяет мне инкапсулировать типы React и при необходимости расширять их собственными.

Dragos 25.05.2021 19:55

У меня всегда работало:

type Props = {
  children: JSX.Element;
};

Я бы рекомендовал просто использовать React.PropsWithChildren<{}> или хотя бы React.ReactNode вместо JSX.Element, поскольку он подходит не только для типа Element.

kimbaudi 10.02.2021 20:15

Эта работа для меня тоже. Я думаю, что набор машинописных текстов сейчас становится беспорядочным.

govo 16.04.2021 08:57

React Node относится к одному из следующих типов:

  • Boolean (игнорируется)
  • null или undefined (игнорируется)
  • Number
  • String
  • React element (результат JSX)
  • Массив любого из вышеперечисленных, возможно, вложенный

Я использую следующие

type Props = { children: React.ReactNode };

const MyComponent: React.FC<Props> = ({children}) => {
  return (
    <div>
      { children }
    </div>
  );

export default MyComponent;

сегодня общее мнение таково, что React.FunctionComponent (или сокращенно React.FC) - это обескураженный. В вашем случае я бы исключил использование React.FC и просто сделал const MyComponent = ({children}: PropsWithChildren<{}>)

kimbaudi 10.02.2021 20:14

propsWithChildren <{}> однако также вводит некоторые кодовые запахи, где {} является любым

Mathijs Segers 12.04.2021 13:23

Если вы используете React.FC, нет необходимости включать тип children, поскольку он уже определен в базовых типах FC.

wentjun 09.09.2021 07:31

Также можно использовать JSX.ElementChildrenAttribute

export default function Layout({children}: JSX.ElementChildrenAttribute) {
    return <div>
        {children}
    </div>
}
  1. вы должны знать, что любой компонент реакции должен возвращает null или React.Element, но тип props.children - React.ReactNode, поэтому вам нужно использовать props.children внутри Element, чтобы babel настраивал конструктор Element.

  2. Второе правило любого компонента реакции состоит в том, что первая буква имени должен должна быть заглавной, чтобы позволить реакции распознавать, что компонент не является тегом html.

так что код должен быть таким.

const Aux = (props: AuxProps) => <>props.children</>;

еще один совет, если вы все еще используете машинописный текст, функциональный компонент должен быть типа React.FC, как это

type Props = {
   title: string;
}

const Aux:React.FC<Props> = (props) =>
(
    <div>
        <h3>{props.title}</h3>
        { props.children }
        {/* children is exist by default in type React.FC */}
    </div>
)
import { ReactNode, FC } from 'react'

type Props = { children: ReactNode }

const App: FC<Props> = ({children}) => (<div>{children}</div>)

Другие вопросы по теме