У меня есть очень простой функциональный компонент:
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 должны иметь один узел-оболочку или возвращать массив узлов.
Компонент <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+ это уже не так, плюс ошибка говорила бы об этом, если бы это было так
Чтобы использовать <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;.
Нет, если children - это массив. В этом случае вам нужно либо обернуть их в один узел, либо использовать React Fragments.
Да, у меня в коде есть: return React.Children.count(children) > 1 ? <>{children}</> : children, но машинописный текст на это жалуется. Заменил просто на <>{children}</>.
Он жалуется, потому что children - это список элементов, который содержит только 1 элемент. Думаю, сработало бы, если бы вы сделали return React.Children.count(children) > 1 ? <>{children}</> : children[0] @sanfilippopablo
Похоже, это не работает в более новых версиях React. Я вошел в консоль и могу подтвердить, что у меня есть детская опора, но даже с <React.Fragment>, окружающим {props.children}, я ничего не возвращаю.
Как насчет того, когда children - это функция?
Обертывание во Фрагмент <> </> - вот в чем дело! Спасибо)
Вот что сработало для меня:
interface Props {
children: JSX.Element[] | JSX.Element
}
Редактировать Я бы порекомендовал использовать вместо этого children: React.ReactNode.
Это недостаточно широкое определение. Ребенок мог быть строкой.
По крайней мере, этот пример сработал для меня, поскольку мой компонент должен был ожидать 1 или несколько JSX.Elements. Спасибо!
Это не будет работать с выражениями JavaScript в качестве дочерних <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. Использование children: ReactNode будет работать.
или используйте PropsWithChildren, как упоминали другие. Я лично предпочитаю его children: React.ReactNode
С сайта 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 тоже неполный по тем же причинам
@PierreFerry Проблема в том, что почти что-либо можно назначить ReactNode. Это не помогает с точки зрения безопасности типов, подобно тому, как вводить children как any - см. Мой отвечать для объяснения.
Спасибо за ответ @ ford04. В моем случае я хочу, чтобы дети имели свободный тип. Мой компонент принимает любые допустимые дочерние элементы React. Так что я считаю, что это все еще тот ответ, который я искал :)
или PropsWithChildren
В замешательстве ... разве это не то, что у OP уже есть?
вы можете объявить свой компонент следующим образом:
const MyComponent: React.FunctionComponent = (props) => {
return props.children;
}
сегодня общее мнение таково, что React.FunctionComponent (или сокращенно React.FC) - это обескураженный. В вашем случае я бы исключил использование React.FC и просто сделал const MyComponent = ({children}: PropsWithChildren<{}>)
Общее способ найти любой тип на собственном примере. Прелесть машинописного текста в том, что у вас есть доступ к типам все, если у вас есть правильные файлы @types/.
Чтобы ответить на этот вопрос, я просто подумал об использовании компонента React, который имеет опору children. Первое, что пришло в голову? Как насчет <div />?
Все, что вам нужно сделать, это открыть vscode и создать новый файл .tsx в проекте реакции с @types/react.
import React from 'react';
export default () => (
<div children = {'test'} />
);
При наведении указателя мыши на опору children отображается тип. А что вы знаете - его тип - ReactNode (в ReactNode[] нет необходимости).
Затем, если вы щелкните определение типа, вы сразу перейдете к определению 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 + щелчок (в окнах) приведет вас к указанному определению типа. Может быть всем известно, но мне пришлось поискать. Спасибо, что научили нас ловить рыбу, а не просто давать рыбу, кстати.
В качестве типа, содержащего дочерние элементы, я использую:
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;
ReactNodechildren: 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 в случае этого типа?
P - это тип реквизита вашего компонента
Да! это прекрасно сработало. interface LayoutProps {} const Layout = ({children}: PropsWithChildren<LayoutProps>) => {...}
Вы можете использовать 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 Спасибо за это. Я добавил к ответу определение плоских массивов. Он должен охватывать и ваш случай. Дай мне знать.
Спасибо! Однако я не думаю, что ReactChildren здесь подходящий. Думаю хватит children: ReactChild | ReactChild[] или просто ReactNode
Я не согласен. Лучше создать тип / интерфейс как ReactNode по сравнению с ReactChild и ReactChildren, поскольку он принимает больше, чем просто ReactChild / ReactChildren. Или, что еще лучше, используйте PropsWithChildren, как уже упоминали другие.
Вы также можете использовать React.PropsWithChildren<P>.
type ComponentWithChildProps = React.PropsWithChildren<{example?: string}>;
Это именно то, чем я занимаюсь, и я думаю, что это более чистый способ использовать правильный набор текста.
Мне нравится это решение, потому что оно расширяемое. type AuthProviderProps = React.PropsWithChildren<{}> позволяет мне инкапсулировать типы React и при необходимости расширять их собственными.
У меня всегда работало:
type Props = {
children: JSX.Element;
};
Я бы рекомендовал просто использовать React.PropsWithChildren<{}> или хотя бы React.ReactNode вместо JSX.Element, поскольку он подходит не только для типа Element.
Эта работа для меня тоже. Я думаю, что набор машинописных текстов сейчас становится беспорядочным.
React Node относится к одному из следующих типов:
Boolean (игнорируется)null или undefined (игнорируется)NumberStringReact 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<{}>)
propsWithChildren <{}> однако также вводит некоторые кодовые запахи, где {} является любым
Если вы используете React.FC, нет необходимости включать тип children, поскольку он уже определен в базовых типах FC.
Также можно использовать JSX.ElementChildrenAttribute
export default function Layout({children}: JSX.ElementChildrenAttribute) {
return <div>
{children}
</div>
}
вы должны знать, что любой компонент реакции должен возвращает null или React.Element, но тип props.children - React.ReactNode, поэтому вам нужно использовать props.children внутри Element, чтобы babel настраивал конструктор Element.
Второе правило любого компонента реакции состоит в том, что первая буква имени должен должна быть заглавной, чтобы позволить реакции распознавать, что компонент не является тегом 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>)
Чтобы не изобретать колесо заново, вы также можете использовать React.FC для определения своего функционального компонента. то есть,
const layout React.FC = (props) => {/*component body*/}