استفاده از React Children prop با تایپ اسکریپت

تعیین تایپ صحیح children prop در React در ابتدا می‌تواند مشکلاتی را ایجاد کند. اگر بخواهیم آن‌ها را به‌عنوان تایپ خاصی از JSX در نظر بگیریم، ممکن است در رندر کامپوننت‌های child با مشکلاتی مواجه شویم. همچنین مشکل پارادوکس انتخاب نیز مطرح است، زیرا گزینه‌های متعددی برای تایپ children prop وجود دارد و همین موضوع ممکن است باعث شود تا هنگام تصمیم‌گیری دچارخستگی شویم. در این مقاله قصد داریم نحوه استفاده از children prop با استفاده از تایپ اسکریپت را باهم بررسی کنیم.

Children در JSX

هنگامی که یک عبارت JSX را با تگ‌های باز و بسته می‌نویسیم، محتوای ارسال شده بین آن‌ها “child” نامیده می‌شود. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Border> Hey, I represent the JSX children! </Border>
<Border> Hey, I represent the JSX children! </Border>
<Border> Hey, I represent the JSX children! </Border>

در این مثال، رشته

Hey, I represent the JSX children!
Hey, I represent the JSX children!به child رندر شده در
Border
Borderاشاره دارد.

برعکس، برای دسترسی به محتوای ارسال شده بین تگ‌های باز و بسته JSX، React از

props.children
props.childrenاستفاده می‌کند. به عنوان مثال،
Border
Borderمی‌تواند children prop را به صورت زیر دریافت کند:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const Border = ({children}) => {
return <div style={{border: "1px solid red"}}>
{children}
</div>
}
const Border = ({children}) => { return <div style={{border: "1px solid red"}}> {children} </div> }
const Border = ({children}) => {
   return <div style={{border: "1px solid red"}}>
      {children}
   </div>
}

Border
Border به این صورت children prop را می‌پذیرد، سپس
children
childrenرا در یک
div
divبا border  استایل
۱px solid red
۱px solid redرندر می‌کند.

این مفهوم، یعنی دریافت و دستکاری محتوای ارسال شده در تگ‌های باز و بسته یک عبارت JSX کاربرد اصلی children prop است.

بررسی تایپ‌های مختلف children که پشتیبانی می‌شوند

به طور دقیق، تعداد انگشت شماری از انواع محتوای پشتیبانی شده وجود دارد که می‌توانند در تگ‌های باز و بسته عبارت JSX مورد استفاده قرار بگیرند. در ادامه تعدادی از پرکاربردترین آن‌ها را باهم بررسی خواهیم کرد.

Strings

همانطور که در مثال زیر می‌بینیم رشته‌ها جزء تایپ‌‌های معتبر children هستند:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<YourComponent> This is a valid child string </YourComponent />
<YourComponent> This is a valid child string </YourComponent />
<YourComponent> This is a valid child string </YourComponent />

باید به این نکته توجه داشته باشیم که رشته

This is a valid child string
This is a valid child stringدر
Your Component
Your Componentای که داریم
props.children
props.childrenخواهد بود.

JSX

همچنین می‌توانیم سایر عناصر JSX را به عنوان childهای معتبر منتقل کنیم. این کار معمولاً هنگام ترکیب اجزای مختلف تودرتو مفید است. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Wrapper>
<YourFirstComponent />
<YourSecondComponent />
</Wrapper>
<Wrapper> <YourFirstComponent /> <YourSecondComponent /> </Wrapper>
<Wrapper>
  <YourFirstComponent />
  <YourSecondComponent />
</Wrapper>

همانطور که در ادامه می‌بینیم، ترکیب تایپ‌های مختلف childها کاملاً قابل قبول است:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Wrapper>
I am a valid string child
<YourFirstComponent />
<YourSecondComponent />
</Wrapper>
<Wrapper> I am a valid string child <YourFirstComponent /> <YourSecondComponent /> </Wrapper>
<Wrapper>
  I am a valid string child
  <YourFirstComponent />
  <YourSecondComponent />
</Wrapper>

عبارات جاوااسکریپت

عبارات جاوااسکریپت به همان اندازه جزو تایپ‌های معتبر childها به‌شمار می‌آیند. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<YourFirstComponent> {myScopedVariableReference} </YourFirstComponent>
<YourFirstComponent> {myScopedVariableReference} </YourFirstComponent>
<YourFirstComponent> {myScopedVariableReference} </YourFirstComponent>

باید به این نکته توجه داشته باشیم که عبارات در

JSX
JSXداخل پرانتز نوشته می‌شوند.

توابع

توابع نیز همانند مواردی که بررسی کردیم جزو تایپ‌های معتبر هستند. مثلا:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<YourFirstComponent>
{() => <div>{myScopedVariableReference}</div>}
</YourFirstComponent>
<YourFirstComponent> {() => <div>{myScopedVariableReference}</div>} </YourFirstComponent>
<YourFirstComponent> 
  {() => <div>{myScopedVariableReference}</div>} 
</YourFirstComponent>

همانطور که می‌بینیم children prop را می‌توانیم با طیف گسترده‌ای از انواع داده‌ها نشان دهیم. ممکن است تمایل داشته باشیم آن‌ها را به صورت دستی تایپ کنیم، مانند:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type Props = {
children: string | JSX.Element | JSX.Element[] | () => JSX.Element
}
const YourComponent = ({children} : Props) => {
return children
}
type Props = { children: string | JSX.Element | JSX.Element[] | () => JSX.Element } const YourComponent = ({children} : Props) => { return children }
type Props = {
  children: string | JSX.Element | JSX.Element[] | () => JSX.Element
}

const YourComponent = ({children} : Props) => {
  return children
}

این موضوع ممکن است ایده خوبی به نظر برسد، اما children prop را به طور کامل نشان نمی‌دهد. همچنین در مورد fragmentها، portalها و مقادیر رندر نادیده گرفته شده، مانند

undefined
undefined،
null
null،
true
trueیا
false
falseنیز سوالاتی مطرح می‌شود.

یک مثال کامل ممکن است چیزی شبیه به کد زیر باشد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
type Props = {
children: ReactNode
}
// source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d076add9f29db350a19bd94c37b197729cc02f87/types/react/index.d.ts
type ReactText = string | number; type ReactChild = ReactElement | ReactText; interface ReactNodeArray extends Array<ReactNode> {} type ReactFragment = {} | ReactNodeArray; type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined; type Props = { children: ReactNode } // source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d076add9f29db350a19bd94c37b197729cc02f87/types/react/index.d.ts
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;

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

type Props = {
  children: ReactNode
}

// source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d076add9f29db350a19bd94c37b197729cc02f87/types/react/index.d.ts

تایپ‌های extend شده برای ReactPortal و ReactElement را می‌بینیم که ممکن است کمی پیچیده به نظر برسند. اما نکته‌ای که وجود دارد این است که در عمل نمی‌خواهیم children prop را به صورت دستی تایپ کنیم. در عوض پیشنهاد می‌شود تا از تایپ‌های رسمی پشتیبانی شده که در ادامه با آن‌ها آشنا خواهیم شد استفاده کنیم.

استفاده از تایپ PropsWithChildren

تایپ

React.PropsWithChildren
React.PropsWithChildrenاینجا component prop را می‌گیرد و یک تایپ union که مناسب تایپ children prop است را برمی‌گرداند. در مثال زیر تعریف تایپ
PropsWithChildren
PropsWithChildrenرا بررسی می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type PropsWithChildren<P> = P & { children?: ReactNode };
type PropsWithChildren<P> = P & { children?: ReactNode };
type PropsWithChildren<P> = P & { children?: ReactNode };

اکنون باید به این سوال پاسخ دهیم که تایپ

propsWithChildren
propsWithChildrenچگونه کار می‌کند.

فرض کنید یک کامپوننت

Foo
Fooبا props
FooProps
FooPropsداریم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type FooProps = {
name: 'foo'
}
export const Foo = (props: FooProps) => {
return null
}
type FooProps = { name: 'foo' } export const Foo = (props: FooProps) => { return null }
type FooProps = {
  name: 'foo'
}

export const Foo = (props: FooProps) => {
    return null
}

می‌توانیم children prop را به صورت زیر تعریف کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { PropsWithChildren } from 'react'
type FooProps = {
name: 'foo'
}
export const Foo = (props: PropsWithChildren<FooProps>) => {
return props.children
}
import { PropsWithChildren } from 'react' type FooProps = { name: 'foo' } export const Foo = (props: PropsWithChildren<FooProps>) => { return props.children }
import { PropsWithChildren } from 'react'

type FooProps = {
  name: 'foo'
}

export const Foo = (props: PropsWithChildren<FooProps>) => {
    return props.children
}

وقتی که

PropsWithChildren
PropsWithChildrenرا به کامپوننت
FooProps
FooPropsمنتقل می‌کنیم، در حقیقت children prop را به صورت داخلی تایپ می‌کنیم.

توسعه‌دهندگان اکثرا استفاده از این روش برای تایپ children prop را توصیه می‌‌کنند. زیرا به boilerplate کم‌تری نیاز دارد و children prop به طور ضمنی تایپ شده است.

استفاده صریح از تایپ ReactNode

در مواردی که باید صریحاً children prop را تایپ کنیم، می‌توانیم از تایپ

ReactNode
ReactNodeاستفاده کنیم. تعریف تایپ
PropsWithChildren
PropsWithChildrenبه صورت زیر است:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type PropsWithChildren<P> = P & { children?: ReactNode };
type PropsWithChildren<P> = P & { children?: ReactNode };
type PropsWithChildren<P> = P & { children?: ReactNode };

propsWithChildren
propsWithChildrenاز تایپ
reactNode
reactNodeاستفاده می‌کند.

همچنین می‌توانیم به‌ جای استفاده از

PropsWithChildren
PropsWithChildren، مستقیماً children prop را نیز تایپ کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { ReactNode } from 'react'
type FooProps = {
name: 'foo'
// look here 👇
children: ReactNode
}
export const Foo = (props: FooProps) => {
return props.children
}
import { ReactNode } from 'react' type FooProps = { name: 'foo' // look here 👇 children: ReactNode } export const Foo = (props: FooProps) => { return props.children }
import { ReactNode } from 'react'

type FooProps = {
  name: 'foo'
  // look here 👇
  children: ReactNode
}

export const Foo = (props: FooProps) => {
    return props.children
}

استفاده از تایپ FunctionComponent (یا FC)

interface عمومی

FunctionComponent
FunctionComponentهمچنین ممکن است به عنوان تایپ مناسب children prop مورد استفاده قرار گیرد. از لحاظ داخلی، این رابط به
PropsWithChildren
PropsWithChildrenمتکی است.

در ادامه نحوه استفاده از آن را بررسی می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { FunctionComponent } from 'react'
type FooProps = {
name: 'foo'
}
export const Foo: FunctionComponent<FooProps> = (props) => {
return props.children
}
import { FunctionComponent } from 'react' type FooProps = { name: 'foo' } export const Foo: FunctionComponent<FooProps> = (props) => { return props.children }
import { FunctionComponent } from 'react'

type FooProps = {
  name: 'foo'
}

export const Foo: FunctionComponent<FooProps> = (props) => {
    return props.children
}

توجه به این نکته لازم است که تایپ

FC
FCیک نام مستعار برای
FunctionComponent
FunctionComponentاست. همانطور که در ادامه می‌بینیم موارد استفاده از آن‌ها شبیه هم می‌باشد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { FC } from 'react'
type FooProps = {
name: 'foo'
}
export const Foo: FC<FooProps> = (props) => {
return props.children
}
import { FC } from 'react' type FooProps = { name: 'foo' } export const Foo: FC<FooProps> = (props) => { return props.children }
import { FC } from 'react'

type FooProps = {
  name: 'foo'
}

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

استفاده از تایپ Component برای کلاس کامپوننت‌ها

اکثر پایگاه‌های کد React مدرن دیگر از کلاس کامپوننت‌ها استفاده نمی‌کنند، مگر در موارد استفاده خاص. اگر متوجه شویم که نیاز به تایپ children prop در یک کلاس کامپوننت داریم، می‌توانیم از پراپ

Component
Componentاستفاده کنیم. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Component } from 'react'
type FooProps = {
name: 'foo'
}
class Foo extends Component<FooProps> {
render() {
return this.props.children
}
}
import { Component } from 'react' type FooProps = { name: 'foo' } class Foo extends Component<FooProps> { render() { return this.props.children } }
import { Component } from 'react'

type FooProps = {
  name: 'foo'
}

class Foo extends Component<FooProps> {
  render() {
    return this.props.children
  }
}

مشابه interface

FunctionComponent
FunctionComponentو نام مستعار
FC
FCآن، تایپ
Component
Componentبه طور خودکار شامل children prop می‌شود.

جمع‌بندی

در این مقاله سعی کردیم نحوه استفاده از children prop با استفاده از تایپ اسکریپت را بررسی کنیم. به طور کلی پیشنهاد می‌شود در صورت امکان، از تایپ

PropsWithChildren
PropsWithChildrenدر پروژه‌های خود استفاده کنیم.

دیدگاه‌ها:

افزودن دیدگاه جدید