DEV Community

Md Mostafizur Rahman
Md Mostafizur Rahman

Posted on

Conditional Props in React

React.js is a popular JavaScript library used for building user interfaces. With its component-based architecture, React allows developers to easily create reusable UI components. In addition, TypeScript is a type-safe superset of JavaScript that helps developers catch errors early on during development. Combining React with TypeScript can lead to even more robust and error-free code. However, in this article, we will explore how to use conditional props in React.js with the help of TypeScript's discrimination union types.

What are Discrimination Union Types?

Discrimination union types are a type of union type that allows you to narrow down the set of possible values of a union based on a common property. In other words, discrimination union types allow you to define a type that depends on the value of a property. This feature is particularly useful when working with React components that have conditional props.

Example

Let's say we have a component called Input that can have two types of props: type="text" and type="number". We can define a union type that represents these two types of props as follows:

type InputProps = { type: 'text'; value: string } | { type: 'number'; value: number };
Enter fullscreen mode Exit fullscreen mode

This defines a type called InputProps that is a union of two types: one with a type property that is a string with a value of text and a value property that is a string, and another with a type property that is a string with a value of number and a value property that is a number. We can use this type to define our Input component like this:

function Input(props: InputProps) {
  return (
    <input type={props.type} value={props.value} />
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, when we use this component, we can pass in either a type="text" or type="number" prop, and the value prop will be typed accordingly:

<Input type="text" value="Hello World" /> // Renders a text input with "Hello World" value
<Input type="number" value={42} /> // Renders a number input with "42" value
<Input type="text" value={42} /> // Error: 'value' must be a string for type='text'
<Input type="number" value="Hello World" // Error: 'value' must be a number for type='number'
Enter fullscreen mode Exit fullscreen mode

Another Example

Let's say we have a component called PaymentMethod that can have two types of props: method="credit-card" and method="paypal". We can define a union type that represents these two types of props as follows:

type PaymentMethodProps =
  | { method: 'credit-card'; cardNumber: string; cvv: string }
  | { method: 'paypal'; email: string };
Enter fullscreen mode Exit fullscreen mode

This defines a type called PaymentMethodProps that is a union of two types: one with a method property that is a string with a value of 'credit-card', a cardNumber property that is a string, and a cvv property that is a string. The other type has a method property that is a string with a value of 'paypal' and an email property that is a string. We can use this type to define our PaymentMethod component like this:

function PaymentMethod(props: PaymentMethodProps) {
  return (
    <div>
      {props.method === 'credit-card' && (
        <>
          <label>Card Number:</label>
          <input type="text" value={props.cardNumber} />
          <label>CVV:</label>
          <input type="text" value={props.cvv} />
        </>
      )}
      {props.method === 'paypal' && (
        <>
          <label>Email:</label>
          <input type="email" value={props.email} />
        </>
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, when we use this component, we can pass in either a method="credit-card" or method="paypal" prop, but not both. Additionally, if method="credit-card", then cardNumber and cvv props must be present, and if method="paypal", then email prop must be present:

<PaymentMethod method="credit-card" cardNumber="1234567890123456" cvv="123" /> // Renders a credit card input form
<PaymentMethod method="paypal" email="example@example.com" /> // Renders a PayPal input form
<PaymentMethod method="credit-card" /> // Error: 'cardNumber' and 'cvv' must be present for method='credit-card'
<PaymentMethod method="paypal" cardNumber="1234567890123456" cvv="123" /> // Error: 'email' must be present for method='paypal'
<PaymentMethod method="credit-card" cardNumber="1234567890123456" cvv="123" email="example@example.com" /> // Error: Only one of 'cardNumber' and 'cvv', or 'email' is allowed
Enter fullscreen mode Exit fullscreen mode

In this example, we defined a union type that represents two types of props that can be passed to the PaymentMethod component. We then used conditional rendering to show either the credit card input form or the PayPal input form based on the method prop value. Lastly, we added constraints to ensure that some props can't be present based on another prop's value.

Top comments (2)

Collapse
 
uramanovich profile image
Ulad Ramanovich

Thanks for sharing! I use this on a daily basis in my code but didn't know the correct name of this construction :)

Collapse
 
maniruzzamanakash profile image
Maniruzzaman Akash

Short and precise. Thanks for sharing.