تعیین تایپ صحیح children prop در React در ابتدا میتواند مشکلاتی را ایجاد کند. اگر بخواهیم آنها را بهعنوان تایپ خاصی از JSX در نظر بگیریم، ممکن است در رندر کامپوننتهای child با مشکلاتی مواجه شویم. همچنین مشکل پارادوکس انتخاب نیز مطرح است، زیرا گزینههای متعددی برای تایپ children prop وجود دارد و همین موضوع ممکن است باعث شود تا هنگام تصمیمگیری دچارخستگی شویم. در این مقاله قصد داریم نحوه استفاده از children prop با استفاده از تایپ اسکریپت را باهم بررسی کنیم.
هنگامی که یک عبارت JSX را با تگهای باز و بسته مینویسیم، محتوای ارسال شده بین آنها “child” نامیده میشود. به عنوان مثال:
<Border> Hey, I represent the JSX children! </Border>
در این مثال، رشته Hey, I represent the JSX children!
به child رندر شده در Border
اشاره دارد.
برعکس، برای دسترسی به محتوای ارسال شده بین تگهای باز و بسته JSX، React از props.children
استفاده میکند. به عنوان مثال، Border
میتواند children prop را به صورت زیر دریافت کند:
const Border = ({children}) => { return <div style={{border: "1px solid red"}}> {children} </div> }
Border
به این صورت children prop را میپذیرد، سپس children
را در یک div
با border استایل ۱px solid red
رندر میکند.
این مفهوم، یعنی دریافت و دستکاری محتوای ارسال شده در تگهای باز و بسته یک عبارت JSX کاربرد اصلی children prop است.
به طور دقیق، تعداد انگشت شماری از انواع محتوای پشتیبانی شده وجود دارد که میتوانند در تگهای باز و بسته عبارت JSX مورد استفاده قرار بگیرند. در ادامه تعدادی از پرکاربردترین آنها را باهم بررسی خواهیم کرد.
همانطور که در مثال زیر میبینیم رشتهها جزء تایپهای معتبر children هستند:
<YourComponent> This is a valid child string </YourComponent />
باید به این نکته توجه داشته باشیم که رشته This is a valid child string
در Your Component
ای که داریم props.children
خواهد بود.
همچنین میتوانیم سایر عناصر JSX را به عنوان childهای معتبر منتقل کنیم. این کار معمولاً هنگام ترکیب اجزای مختلف تودرتو مفید است. به عنوان مثال:
<Wrapper> <YourFirstComponent /> <YourSecondComponent /> </Wrapper>
همانطور که در ادامه میبینیم، ترکیب تایپهای مختلف childها کاملاً قابل قبول است:
<Wrapper> I am a valid string child <YourFirstComponent /> <YourSecondComponent /> </Wrapper>
عبارات جاوااسکریپت به همان اندازه جزو تایپهای معتبر childها بهشمار میآیند. به عنوان مثال:
<YourFirstComponent> {myScopedVariableReference} </YourFirstComponent>
باید به این نکته توجه داشته باشیم که عبارات در JSX
داخل پرانتز نوشته میشوند.
توابع نیز همانند مواردی که بررسی کردیم جزو تایپهای معتبر هستند. مثلا:
<YourFirstComponent> {() => <div>{myScopedVariableReference}</div>} </YourFirstComponent>
همانطور که میبینیم children prop را میتوانیم با طیف گستردهای از انواع دادهها نشان دهیم. ممکن است تمایل داشته باشیم آنها را به صورت دستی تایپ کنیم، مانند:
type Props = { children: string | JSX.Element | JSX.Element[] | () => JSX.Element } const YourComponent = ({children} : Props) => { return children }
این موضوع ممکن است ایده خوبی به نظر برسد، اما children prop را به طور کامل نشان نمیدهد. همچنین در مورد fragmentها، portalها و مقادیر رندر نادیده گرفته شده، مانند undefined
، null
، true
یا false
نیز سوالاتی مطرح میشود.
یک مثال کامل ممکن است چیزی شبیه به کد زیر باشد:
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 را به صورت دستی تایپ کنیم. در عوض پیشنهاد میشود تا از تایپهای رسمی پشتیبانی شده که در ادامه با آنها آشنا خواهیم شد استفاده کنیم.
تایپ React.PropsWithChildren
اینجا component prop را میگیرد و یک تایپ union که مناسب تایپ children prop است را برمیگرداند. در مثال زیر تعریف تایپ PropsWithChildren
را بررسی میکنیم:
type PropsWithChildren<P> = P & { children?: ReactNode };
اکنون باید به این سوال پاسخ دهیم که تایپ propsWithChildren
چگونه کار میکند.
فرض کنید یک کامپوننت Foo
با props FooProps
داریم:
type FooProps = { name: 'foo' } export const Foo = (props: FooProps) => { return null }
میتوانیم children prop را به صورت زیر تعریف کنیم:
import { PropsWithChildren } from 'react' type FooProps = { name: 'foo' } export const Foo = (props: PropsWithChildren<FooProps>) => { return props.children }
وقتی که PropsWithChildren
را به کامپوننت FooProps
منتقل میکنیم، در حقیقت children prop را به صورت داخلی تایپ میکنیم.
توسعهدهندگان اکثرا استفاده از این روش برای تایپ children prop را توصیه میکنند. زیرا به boilerplate کمتری نیاز دارد و children prop به طور ضمنی تایپ شده است.
در مواردی که باید صریحاً children prop را تایپ کنیم، میتوانیم از تایپ ReactNode
استفاده کنیم. تعریف تایپ PropsWithChildren
به صورت زیر است:
type PropsWithChildren<P> = P & { children?: ReactNode };
propsWithChildren
از تایپ reactNode
استفاده میکند.
همچنین میتوانیم به جای استفاده از PropsWithChildren
، مستقیماً children prop را نیز تایپ کنیم:
import { ReactNode } from 'react' type FooProps = { name: 'foo' // look here 👇 children: ReactNode } export const Foo = (props: FooProps) => { return props.children }
interface عمومی FunctionComponent
همچنین ممکن است به عنوان تایپ مناسب children prop مورد استفاده قرار گیرد. از لحاظ داخلی، این رابط به PropsWithChildren
متکی است.
در ادامه نحوه استفاده از آن را بررسی میکنیم:
import { FunctionComponent } from 'react' type FooProps = { name: 'foo' } export const Foo: FunctionComponent<FooProps> = (props) => { return props.children }
توجه به این نکته لازم است که تایپ FC
یک نام مستعار برای FunctionComponent
است. همانطور که در ادامه میبینیم موارد استفاده از آنها شبیه هم میباشد:
import { FC } from 'react' type FooProps = { name: 'foo' } export const Foo: FC<FooProps> = (props) => { return props.children }
اکثر پایگاههای کد React مدرن دیگر از کلاس کامپوننتها استفاده نمیکنند، مگر در موارد استفاده خاص. اگر متوجه شویم که نیاز به تایپ children prop در یک کلاس کامپوننت داریم، میتوانیم از پراپ Component
استفاده کنیم. به عنوان مثال:
import { Component } from 'react' type FooProps = { name: 'foo' } class Foo extends Component<FooProps> { render() { return this.props.children } }
مشابه interface FunctionComponent
و نام مستعار FC
آن، تایپ Component
به طور خودکار شامل children prop میشود.
در این مقاله سعی کردیم نحوه استفاده از children prop با استفاده از تایپ اسکریپت را بررسی کنیم. به طور کلی پیشنهاد میشود در صورت امکان، از تایپ PropsWithChildren
در پروژههای خود استفاده کنیم.