TypeScript dan React - tipe anak-anak?


138

Saya memiliki komponen fungsional yang sangat sederhana sebagai berikut:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


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

export default aux;

Dan komponen lainnya:

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;

Saya terus mendapatkan kesalahan berikut:

[ts] Jenis elemen JSX 'ReactNode' bukanlah fungsi konstruktor untuk elemen JSX. Ketik 'undefined' tidak dapat digunakan untuk mengetik 'ElementClass'. [2605]

Bagaimana cara saya mengetik ini dengan benar?

Jawaban:


133

Hanya children: React.ReactNode


1
Inilah jawaban yang benar di sini! JSX.Elementtidak cukup baik karena anak-anak React yang valid dapat berupa string, boolean, null ... ReactChildjuga tidak lengkap karena alasan yang sama
Pierre Ferry

2
@PierreFerry Masalahnya adalah, hampir semua hal bisa ditugaskan ReactNode. Itu tidak membantu dalam hal keamanan tipe, mirip dengan mengetik childrensebagai any- lihat jawaban saya untuk penjelasan.
ford04

Terima kasih atas tanggapan Anda @ ford04. Dalam kasus penggunaan saya, saya ingin anak-anak menjadi tipe yang longgar. Komponen saya menerima semua jenis turunan React yang valid. Jadi saya yakin ini masih jawaban yang saya cari :)
Pierre Ferry

47

Untuk dapat digunakan <Aux>di JSX Anda, ini harus berupa fungsi yang mengembalikan ReactElement<any> | null. Itulah definisi komponen fungsi .

Namun, saat ini didefinisikan sebagai fungsi yang mengembalikan React.ReactNode, yang merupakan tipe yang jauh lebih luas. Seperti yang dikatakan pengetikan React:

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

Pastikan tipe yang tidak diinginkan dinetralkan dengan membungkus nilai yang dikembalikan ke dalam React Fragment ( <></>):

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

Saya tidak mengerti mengapa itu perlu dibungkus childrendalam Fragmen React. Mau menjelaskan? Di React, ini sangat valid untuk hanya return children;.
sanfilippopablo

1
Tidak jika childrenadalah sebuah array. Jika demikian, Anda perlu membungkusnya dalam satu node atau menggunakan React Fragments.
Karol Majewski

Ya, dalam kode saya, saya punya return React.Children.count(children) > 1 ? <>{children}</> : children:, tapi naskah ketikan mengeluh tentang itu. Saya menggantinya dengan hanya <>{children}</>.
sanfilippopablo

2
Ia mengeluh karena childrenmerupakan daftar elemen yang kebetulan hanya berisi 1 item. Saya pikir itu akan berhasil jika Anda melakukannya return React.Children.count(children) > 1 ? <>{children}</> : children[0]@sanfilippopablo
tamj0rd2

Ini tampaknya tidak berfungsi di versi React yang lebih baru. Saya konsol login dan dapat mengonfirmasi bahwa saya memiliki prop anak, tetapi bahkan dengan <React.Fragment>sekitarnya {props.children}saya tidak mengembalikan apa pun.
Tandai

34

Inilah yang berhasil untuk saya:

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

Edit Saya akan merekomendasikan menggunakan children: React.ReactNodesebagai gantinya sekarang.


8
Definisi itu tidak cukup luas. Anak itu bisa menjadi tali.
Mike S

Setidaknya contoh ini berhasil untuk saya karena komponen saya diharapkan mengharapkan 1 atau lebih JSX.Elements. Terima kasih!
Italo Borges

1
Ini tidak akan berfungsi dengan ekspresi JavaScript sebagai anak-anak <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. Menggunakan children: ReactNodeakan berhasil.
Nickofthyme

10

Anda dapat mendeklarasikan komponen Anda seperti ini:

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

9

Anda dapat menggunakan ReactChildrendan ReactChild:

import React, { ReactChildren, ReactChild } from 'react';

interface AuxProps {
  children: ReactChild | ReactChildren;
}

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

export default Aux;


6

Jenis pengembalian komponen fungsi terbatas JSXElement | nullpada TypeScript. Ini adalah batasan tipe saat ini, React murni memungkinkan lebih banyak tipe pengembalian.

Cuplikan demonstrasi minimal

Anda dapat menggunakan pernyataan tipe atau Fragmen sebagai solusi:

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

ReactNode

children: React.ReactNodemungkin suboptimal, jika tujuannya adalah untuk memiliki tipe yang kuat Aux.

Hampir semua hal dapat ditetapkan ke ReactNodetipe saat ini , yang setara dengan {} | undefined | null. Jenis yang lebih aman untuk casing Anda bisa jadi:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Contoh:

Mengingat Auxkebutuhan elemen React seperti children, kami secara tidak sengaja menambahkan a stringke dalamnya. Maka solusi di atas akan salah berbeda dengan ReactNode- lihatlah taman bermain yang terhubung.

Diketik childrenjuga berguna untuk props non-JSX, seperti callback Render Prop .


5

Anda juga bisa menggunakan React.PropsWithChildren<P>.

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

3

Cara umum untuk menemukan tipe apa pun adalah dengan contoh. Keindahan dari naskah ketikan adalah Anda memiliki akses ke semua jenis, selama Anda memiliki @types/file yang benar .

Untuk menjawab ini sendiri, saya hanya memikirkan penggunaan reaksi komponen yang memiliki childrenprop. Hal pertama yang terlintas dalam pikiran? Bagaimana dengan a <div />?

Yang perlu Anda lakukan hanyalah membuka vscode dan membuat .tsxfile baru dalam proyek react dengan @types/react.

import React from 'react';

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

Melayang di atas childrenpenyangga menunjukkan jenisnya. Dan apa yang Anda ketahui - Jenisnya adalah ReactNode(tidak perlu ReactNode[]).

masukkan deskripsi gambar di sini

Kemudian jika Anda mengklik definisi tipe, Anda akan langsung menuju definisi yang childrenberasal dari DOMAttributesantarmuka.

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

Catatan: Proses ini harus digunakan untuk menemukan jenis yang tidak diketahui! Semuanya ada di sana menunggu Anda untuk menemukannya :)


2

Sebagai tipe yang berisi anak-anak, saya menggunakan:

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

Jenis penampung anak ini cukup umum untuk mendukung semua kasus yang berbeda dan juga selaras dengan API ReactJS.

Jadi, untuk contoh Anda, itu akan menjadi seperti ini:

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

1

Jawaban ini tampaknya sudah usang - React sekarang memiliki tipe bawaan PropsWithChildren<{}>. Ini didefinisikan serupa dengan beberapa jawaban yang benar di halaman ini:

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


1
Apa Pkasus tipe itu?
sunpietro

Padalah jenis alat peraga komponen Anda
Tim Iles

-2

Komponen React harus memiliki node pembungkus tunggal atau mengembalikan array node.

Anda <Aux>...</Aux>Komponen memiliki dua node divdan main.

Cobalah untuk membungkus anak Anda dalam divdi Auxkomponen.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

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

export default aux;

1
Tidak benar. Di React 16+ ini tidak lagi menjadi masalah, ditambah kesalahan akan mengatakan ini jika memang demikian
Andrew Li
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.