Conditional Props in React
Published: 2023-04-16 13:45:57

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:

```typescript
type InputProps = { type: 'text'; value: string } | { type: 'number'; value: number };
```
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:

```tsx
function Input(props: InputProps) {
  return (
    <input type={props.type} value={props.value} />
  );
}
```
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:

```tsx
<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'
```

## 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:

```tsx
type PaymentMethodProps =
  | { method: 'credit-card'; cardNumber: string; cvv: string }
  | { method: 'paypal'; email: string };
```
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:

```tsx
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>
  );
}
```
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:

```tsx
<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
```
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.