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>
);
}
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!
<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.