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,
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.
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 one.
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 InputProps = JSX.IntrinsicElements['input'] // new
type Props = InputProps & { labelText: string } // new
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 InputProps = JSX.IntrinsicElements['input']
type Props = InputProps & { labelText: string }
export const Input = (props: Props) => {
return (
<label>
<span>{props.labelText}</span>
<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 }
export const Input = (props: Props) => {
const { labelText, ...inputProps} = props
return (
<label>
<span>{labelText}</span>
<input {...inputProps} />
</label>
)
}
Ta-da! now it’s dead clean.
You can keep code tidy by using spread syntax even if you end up having more props.