primers

React Advanced React API Introduction and Basic Practice Since I use Ts to implement the example in the React Advanced blog, I encountered Ts error when implementing the cloneElement API example.

The following error message is displayed:

No overload matches this call.
  The last overload gave the following error.
    Argument of type 'ReactNode' is not assignable to parameter of type 'ReactElement<any, string | JSXElementConstructor<any>>'.
      Type 'undefined' is not assignable to type 'ReactElement<any, string | JSXElementConstructor<any>>'.
Copy the code

CloneElement: ReactElement; ReactElement: ReactElement; ReactElement: ReactElement; ReactElement: ReactElement;

This situation got me thinking, what is the relationship between ReactNode and ReactElement?

ReactNode, ReactElement

Looking at the declaration file, we can conclude that the ReactNode type is as follows:

    type ReactText = string | number;
    type ReactChild = ReactElement | ReactText;

    interface ReactNodeArray extends Array<ReactNode> {}
    type ReactFragment = {} | ReactNodeArray;
    type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
Copy the code

We found that ReactNode is a union type, and the types include ReactChild, ReactFragment, ReactPortal, Boolean, NULL, and undefined.

Among the union types of ReactNode, ReactChild is also a union type. The type is ReactElement or ReactText.

The fact that ReactElement is nothing more than a subtype of ReactNode is obvious.

Let’s take a look at ReactElement:

    interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
        type: T;
        props: P;
        key: Key | null;
    }
Copy the code

This is an interface that has properties such as Type, props, and key.

JSX.Element

Jsx. Element is obtained by executing React. CreateElement or by translating JSX.

const jsx = <div>hello</div>
const ele = React.createElement("div".null."hello");
<p> // <- ReactElement = JSX.Element
  <Custom> // <- ReactElement = JSX.Element
    {true && "test"} // <- ReactNode
  </Custom>
</p>
Copy the code

Element is a ReactElement whose props and type generics are set to any. Jsx. Element exists because different libraries implement JSX differently, that is, JSX is a global namespace. React is set up in different libraries as follows:

declare global {
  namespace JSX {
    interface Element extends React.ReactElement<any, any> { }
  }
}
Copy the code

summary

From the above analysis, we can conclude that ReactElement is a subset of ReactNodes. In React jsX. Element and ReactElement are almost equivalent. The way to implement jsx. Element in React is ReactElement.

To solve the problem

The cause of this problem becomes clear after we identify ReactNode and ReactElement.

In the figure below, the children type is ReactNode, and the cloneElement method accepts the children type as a ReactElement.

So we need to set the children type to be a ReactElement.

As shown in the figure above, we can restrict the type of children to ReactElement by generics.

This way, if we write code without paying attention to the children type and mistakenly provide a children of ReactText, Ts will check for the error.

You see, the benefits of Ts are quite a lot.

reference

  • when-to-use-jsx-element-vs-reactnode-vs-reactelement
  • #cloneElement #cloneElement