開源日報每天推薦一個 GitHub 優質開源項目和一篇精選英文科技或編程文章原文,堅持閱讀《開源日報》,保持每日學習的好習慣。
開源日報第1120期:React Native通用 UI 《react-native-reusables》
2024年2月29日,開源日報第1120期:
今日推薦開源項目:《react-native-reusables》
今日推薦英文原文:《How come I didn』t know this Typescript trick??!》


開源項目

今日推薦開源項目:《react-native-reusables》傳送門:項目鏈接

推薦理由:React Native的通用shadcn/ui:複製、粘貼和調整組件

直達鏈接: rnr-docs.vercel.app


英文原文

今日推薦英文原文:How come I didn』t know this Typescript trick??!

推薦理由: 如何使用 TypeScript 在組件中接受動態屬性,並根據其他屬性的值來確定是否顯示某些內容這種需求在開發中是常見的,文章這個例子展示如何實現根據用戶提供的位置來決定是否提供免費送貨服務的功能,代碼注釋很詳細


How come I didn』t know this Typescript trick??!

Today, I want to show you how to accept dynamic props to any component, (i.e accepting props based on other props value).

Assuming you were asked to create a product page that accepts some props (e.g location, productName, etc.). Now, we may want to have a free shipping feature if the user provided their location in the form.

First, our solution might be creating and adding optional location value to determine the interaface, and then check for the lcoation value to conditionally render the free shipping feature.

type AcceptedCountries = "Nigeria" | "Brazil" | "Spain";

type ProductProps = {
  name: string;
  price: number;
  location: AcceptedCountries
  isEligbleForFreeShipping: boolean;
}

const Product = (props: ProductProps) => {
  return (
    <>
        <div>{props.name}</div>
        <div>{props.price}</div>
        {location &&
         <div>{props.isEligbleForFreeShipping 
            ? "Free Shipping" : "No Free Shipping"}
         </div>}
    </>
  )
}

const Page = () => {
  return (
    <>
      <Product
       name="Macbook M2"
       price={2000} 
      isEligbleForFreeShipping={true}
      />
    </>
  )
}

This way, we don』t get any type safety on the Page component level on how it is rendering the Product component.
In the Product component, we checked to see if they provided a location to determine if they are eligble for free shipping.

🤔 Think for a second, We would want typescript to yell at us if we provide the isEligbleForFreeShipping prop without providing a location, because we only want users that stays within AcceptedCountries to be eligble for free shipping.

Now, Let』s refactor the ProductProps』 interface to achieve that.

type AcceptedCountries = "Nigeria" | "Brazil" | "Spain";

type ProductProps = {
  name: string;
  price: number;

} & {
  location: AcceptedCountries
  isEligbleForFreeShipping: boolean;
   }

Now, we made an extension to the interfae, this adds another interface to the ProductProps that checks if the user provides a location, and then expose the isEligbleForFreeShipping prop.
We』re now ready to use this in our application.

const Product = (props: ProductProps) => {
  let name, price, isEligbleForFreeShipping;

   if (props.location){
    isEligbleForFreeShipping = props.isEligbleForFreeShipping
  }
  return (
    <>
        <div>{props.name}</div>
        <div>{props.price}</div>
         <div>{isEligbleForFreeShipping
            ? "Free Shipping" : "No Free Shipping"}
         </div>
    </>
  )
}

Now, our business logic is now very synthetic. How?

  • We moved the location check to display free shipping message check to Type level

We are now ready to use this component in the main page:

const Page = () => {
  return (
    <>
      <Product
       name="Macbook M2"
       price={2000} 
       location="Nigeria"
      isEligbleForFreeShipping={true}
      />
    </>
  )
}

Here, If we try to pass the isEligbleForFreeShipping prop without providing a location, Typescript throws an error. This way, we are sure of what prop we receive based on the other props value.

This is the most exciting part. 💃

We can take this a bit further to give benefits to users in individual countries.
For example, we want to allow customers from a Spain to return the goods ater it has been purchased, while we only want to offer a free shipping to users in Nigeria.
Now, we can modify our codes as follows

const CountriesWithBenefits = ["Nigeria", "Brazil", "Spain"] as const

type CountryIsSpain = CountriesWithBenefits[2]
type CountryIsNigeria = CountriesWithBenefits[0]

type ProductProps = {
  name: string;
  price: number;
} & (CanReturnProducts | HasFreeShipping)

type CanReturnProducts = {
  location: CountryIsSpain
  feedbackText: string
}

type HasFreeShipping = {
  location: CountryIsNigeria
  thankYouMessage: string
}
const Product = (props: ProductProps) => {
  let name, price, feedbackText, thankYouMessage;

   if (props.location === "Nigeria"){
    thankYouMessage = props.thankYouMessage
  }

if (props.location === "Spain"){
    feedbackText = props.feedbackText
  }

  return (
    <>
        <div>{props.name}</div>
        <div>{props.price}</div>
        {/** Now, we can use any of the values we iniialized here or in another component*/}
    </>
  )
}

const Page = () => {
  return (
    <>
      <Product
       name="Macbook M2"
       price={2000} 
       location="Nigeria"
       thankYouMessage={"Hey! Your product would get to you in a few"}
      />

      {/** Usage for customers in Spain*/}

      <Product
       name="Macbook M2"
       price={2000} 
       location="Spain"
       feedbackText={"The screen resolution isn't alright :("}
      />
    </>
  )
}

下載開源日報APP:https://openingsource.org/2579/
加入我們:https://openingsource.org/about/join/
關注我們:https://openingsource.org/about/love/