Почему React.F C не позволяет мне просто возвращать детей? - PullRequest
3 голосов
/ 20 июня 2020

Пытаясь создать компонент, я понял ситуацию.

Возвращается только children

interface FooProps {
  children?: React.ReactNode
}

const Foo: React.FC<FooProps> = ({ children }) => {
  return children
}

Выдает сообщение об ошибке:

 Type '({ children }: PropsWithChildren<FooProps>) => ReactNode' is not assignable to type 'FC<FooProps>'.  
  Type 'ReactNode' is not assignable to type 'ReactElement<any, any> | null'.  
    Type 'undefined' is not assignable to type 'ReactElement<any, any> | null'.  

Но если я верну children внутри любого jsx или даже Fragment:

const Foo: React.FC<FooProps> = ({ children }) => {
  return <>{children}</>
}

, это не даст мне никакой ошибки.

Очевидный ответ на это - типы несовместимы, ReactNode нельзя присвоить типу ReactElement<any, any> | null, как говорится в сообщении об ошибке, но мой вопрос: почему?

Почему возврат ReactNode (например, children) не разрешен? Разве это не должно быть разрешено?

Что-то еще, о чем следует спросить, - это что-то вроде React.FC и, вероятно, другой тип будет в порядке, если я верну ReactNode или это будет со всеми реагирующими компонентами?

1 Ответ

1 голос
/ 20 июня 2020

Потому что тип React.ReactNode - это тип объединения. Давайте посмотрим, как выглядит определение типа для него:

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

Ожидаемый тип возврата при создании функционального компонента с generi c FC

ReactElement<any, any> | null

Итак, когда вы возвращаете children непосредственно из компонента, компилятор жалуется, потому что типы просто несовместимы. Причина в том, что ReactElement является действительным React.ReactNode, но не наоборот, потому что React.ReactNode также может быть значением типа ReactFragment или ReactPortal и т. Д.

Это очевидно что это создаст несоответствие типов с ReactElement. Но когда вы возвращаете children внутри Fragment, компилятор больше не жалуется, потому что возвращаемый тип становится действительным. Взгляните на этот пример без FC generi c,

// The return type of Foo is inferred as React.ReactNode
// Compiler doesn't complain because we don't annotate the return type
const Foo = ({ children }: PropsWithChildren<FooProps>) => {
  return children;
};

// But when you use React.FC generic it is the same as annotating the return type
// of Foo as `React.ReactElement`

// compiler will complain because of the type mismatch
const Foo = ({ children }: PropsWithChildren<FooProps>): React.ReactElement => {
  return children;
};

Пример того, почему это работает, когда вы возвращаете children внутри Fragment

// Without generic FC
// Return type infered as JSX.Element which simply extends React.ReactElement
const Foo = ({ children }: PropsWithChildren<FooProps>) => {
  return <>children</>;
};

// With generic FC
// Compiler doesn't complain because of JSX.Element can be valid-
// return type because it simply extends the interface React.ReactElement 
const Foo: FC<FooProps> = ({ children }) => {
  return <>children</>;
};

Изменить - Возможно, это не прямой ответ, но я надеюсь, что этот пример может больше объяснить, почему React.ReactNode нельзя разрешать.

React.ReactNode является широким, и вы можете назначить ему практически что угодно. Например,

const Foo = () => { return 45; };

// no compile time error because even a functions are a valid ReactNode
const Bar: React.ReactNode = Foo

Но что происходит, когда мы пытаемся отобразить его внутри любого допустимого компонента

Во время компиляции - Нет ошибки

const FooBar: React.FC<FooProps> = () => {
  // again no compile time error because Bar is a valid ReactNode
  return <div>{Bar}</div>
}

Во время выполнения - Функции ошибок недопустимы, реагировать на дочерние элементы

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