Hello World. We are WEB cult. We make a note for web developers.

Write fewer React props passing to input element

Written or Updated on August 01, 2022 🖋️

Presentational VS Container

We use two types of component as a convention of React.

Presentational components

  • ・are concerned how to display data (props)
  • ・have Markup and style
  • ・have no dependencies on Flux flow
  • ・don’t load data or mutate data
  • ・are usually stateless

Container components

  • ・are concerned how things work
  • ・have NO markup nor style
  • ・are connected with React flux flow
  • ・communicate with an external source
  • ・are usually stateful

This structure makes React more robust and simple, but we end up writing and passing so many props.

I’m gonna leave a note how to write fewer props.

Make a form

Let’s say we have to make a sign up form.

make a input.tsx,

// input.tsx

import React from 'react'

interface Props {
  labelText: string
  type: string
  name: string
}

export const Input = (props: Props) => {
  const { labelText, type, name } = props

  return (
    <label>
      <span>{labelText}</span>
      <input type={type} name={name} />
    </label>
  )
}

then import it and render it inside form.tsx.

// form.tsx

import React from 'react'
import { Input } from './input'

export const Form = () => {
  return (
    <div>
      <h1>Sign up form</h1>
      <Input labelText={'First name'} type={'text'} name={'firstName'} /> 
      <Input labelText={'Last name'} type={'text'} name={'lastName'} /> 
      <Input labelText={'email'} type={'email'} name={'email'} /> 
      <Input labelText={'password'} type={'password'} name={'password'} />
    </div>
  );
}

form

This is what we do usually, right?

More props

I wish we could finish by it, but we need to write more and more props in reality.

like this,

import React from 'react'

interface Props {
  labelText: string
  type: string
  name: string
  placeholder: string
  disabled: boolean
  required: boolean
  onChange: React.ChangeEventHandler<HTMLInputElement>
}

export const Input = (props: Props) => {
  const { 
    labelText, 
    type, 
    name, 
    placeholder,
    disabled,
    required,
    onChange
  } = props

  return (
    <label>
      <span>{labelText}</span>
      <input 
        type={type} 
        name={name} 
        placeholder={placeholder}
        disabled={disabled}
        required={required}
        onChange={onChange}
      />
    </label>
  )
}

I added some realistic props and it’s gonna be messier by time.

Let’s clean up and make it simple.

This time, I’m gonna use this type definition.

JSX.IntrinsicElements[‘input’]

By this, You can get all attributes which input element has. So props definition becomes much better.

import React from 'react'

+ type Props = JSX.IntrinsicElements['input']

export const Input = (props: Props) => {
  const { 
    labelText, 
    type, 
    name, 
    placeholder,
    disabled,
    required,
    onChange
  } = props

  return (
    <label>
      <span>{labelText}</span>
      <input 
        type={type} 
        name={name} 
        placeholder={placeholder}
        disabled={disabled}
        required={required}
        onChange={onChange}
      />
    </label>
  )
}

Looks nice but not yet!
You can pass all props to <input> by spread syntax at once.

import React from 'react'

type Props = JSX.IntrinsicElements['input']

export const Input = (props: Props) => {
-   const { 
-     labelText, 
-     type, 
-     name, 
-     placeholder,
-     disabled,
-     required,
-     onChange
-   } = props
  
  return (
    <label>
      <span>{props.labelText}</span>
-       <input 
-         type={type} 
-         name={name} 
-         placeholder={placeholder}
-         disabled={disabled}
-         required={required}
-         onChange={onChange}
-       />
+       <input {...props} />
    </label>
  )
}

Almost done but there is just one more thing to do!

warning

<input> doesn’t have any attributes called labelText so React is complaining about it.
Let’s extract labelText from props.
You can use spread syntax again.

import React from 'react'

type InputProps = JSX.IntrinsicElements['input']
type Props = InputProps & { labelText: string } // <= here

export const Input = (props: Props) => {
  const { labelText, ...inputProps} = props
  
  return (
    <label>
      <span>{labelText}</span>
      <input {...inputProps} />
    </label>
  )
}

Now it’s very clean.
You can keep code tidy by using spread syntax even if you end up having more props.