开源日报每天推荐一个 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/