Controlled & Uncontrolled React Components
Digging deep inside the stateless and stateful React components.
If you have been using React some of the time you must have heard about controlled and uncontrolled components.
As we know there are two directions for state flowing between components the down-to-up and up-to-down.
The down-to-up (from child to parent component) is more straightforward and always can be done by callbacks, the down component triggers a callback and the up component listens to it.
The up-to-down direction (from parent to child component) can be done by passing the state to the prop of the child component, once you mutate that state, the behavior of the child component should change, that might be a bit tricky than the first one.
Controlled Component.
The controlled component is the component that its main state is controlled from outside.
Technically is the component that takes its current value through props and triggers changes through callbacks like onChange
.
A parent component “controls” it by handling the callback and managing its own state and passing the new values as props to the controlled component. You could also call this a “dumb component”.
Example
Here’s a working code of a controlled component where we have displayed text.
Let’s understand the important aspects of controlled components from this example —
- When the input change occurs, the input element fires the event handler
onChange
and passes the new value of the text input. - The external component receives that new value through
onChange
listener and and passes the new value to the parent component state. - Then the parent component which is
ParentComponent
passes the new state to the child, because the displayed message comes from parent state.
The state flow is unidirectional from the component with external components.
You can call it a Stateless component because the component doesn’t store or hold the main state inside it and is state-controlled from the outside.
Uncontrolled Component.
An Uncontrolled Component is one that stores its own state internally.
Usually, that type of component has initialValue
prop just for passing the initial value of the state in the first rendering, but won’t be able to control the internal stored state in the further mutations.
Example
Here’s a working code of an uncontrolled component where we have displayed text.
If you look at the code the state flowing as the following.
- The
initialValue
prop just for initial value of value state, if you mutated that state from outside the value state won’t change. - The parent component can listen the child component by
onChange
and the callback passes the new value of the child component.
An Uncontrolled Component should be a Stateful because the component stores and holds the main state inside and the main state is uncontrollable from the outside.
Controlled & Uncontrolled Component.
If you have gone through controlled and uncontrolled component you may have already guessed when we should use controlled or uncontrolled component, actually that depends on the use-case of that component.
It’s preferable to build components to be controllable and uncontrollable at the same time out-of-the-box, which will give flexibility to the end user to choose between two approaches.
Let’s refactor the component that we already wrote to be controlled and uncontrolled at the same time.
Let’s review the major aspects of the code.
- If the user passed any value to the
value
prop the component will convert to be controlled from outside. and that is obviously done by the condition on thelocalValue
variable. - The
uncontrolledValue
state holds the inner component’s value and that state will be neglected if the user passed a value to the value prop because the component will takelocalValue
state fromvalue
prop.
Refactor the Component to be Reusable Hook
We want to build a custom hook to be more reusable and and that hook should receive 3 main inputs which are the value
, initialValue
, onChange
we need all these props for any component either controlled or uncontrolled.
And the hook should output localValue
and onChange
, the localValue represents the local state of the component and might be from the value prop (in case controlled) or from the uncontrolledValue
(in case uncontrolled).
Let’s use that hook in the real world.