In an earlier lesson, we looked at uncontrolled inputs. To jog your memory, these are inputs that can have different states for the same set of props. For example, an input
element without a value
prop will look different after you’ve typed in it, so it is uncontrolled.
In fact, it’s not just inputs that can be uncontrolled. Class components that store state can be uncontrolled too. And this raises the question:
Are stateful class components… the dark side?
Let’s consider the App
component from your contact list app. It fetches an array of your billionaire friends in componentDidMount()
… and stores the array under this.state
.
But the thing about your App
component is that the state never leaves it. Unlike uncontrolled inputs, your App
component doesn’t have any callbacks. The state is contained.
You can’t escape the fact that one way or another, a useful app is going to have state. The trick is, you never want that state to escape its container component.
Container components, or containers, are a name that people use for components that primarly manage state. Containers don’t usually render any DOM elements, instead passing their state to child components that handle presentation.
As you’ve probably guessed, your App
component is a container component.
In contrast, presentational components are those that handle markup and styling. They don’t usually have any state. They can be class components or function components. And they can have callbacks — but the handlers just forward events to parent components via callback props.
Your contact app has a number of presentational components. In fact, the very first components that you build — Contact
and ContactList
— are presentational components.
But what about your ContactForm
component?
Let’s take another look at your ContactForm
component.
When the user clicks the “Add” button, a contact details object is passed out as an argument to onAddContact()
. For example, if you were to add a mythical Japanese character to your contacts, the call to onAddContact()
may look something like this:
this.props.onAddContact({
name: "Momo Taro",
email: "momotaro@example.com",
})
The problem with this is that the state is now in two places:
onAddContact
function just left itNow you may be thinking, but you could just clear the form — then it would only be in one place again? And sure, this might work. But then again, it might not. Can you think of some reasons why?
The form component itself has no control over what the onAddContact
function does.
If you clear the form whenever “Add” is pressed, the user could lose their data!
The problem with uncontrolled components is that they cause state to be in multiple places.
Luckily, you rarely ever actually need an uncontrolled component. And if you do end up with one by accident (or because I didn’t explain things properly the first time), then there’s a simple process to fix it.
Whenever you find yourself with an uncontrolled component, you can convert it to a controlled component by moving, or lifting, the state into a parent component.
To lift state out of a component, you’ll need to do three things:
this.props
instead of this.state
.onChange
prop, instead of calling this.setState()
.Let’s practice this by rewriting your contact form.
To start you off, I’ve updated the propTypes
object to your ContactForm
component, detailing what the new props should look like. I’ve also added a newContact
object to your App
component’s state.
Your task is to:
this.state
from your ContactForm
component.ContactForm
component.App
component.A few hints:
onSubmit
handler doesn’t need any arguments. The form component just needs to notify its parent that “submit” was clicked.this.props.value
, and pass that as an argument to this.props.onChange
.You’ll know when you’re done, as everything will work as expected, and ContactForm
will no longer have any references to this.state
!
If you do get stuck, take a peek at my solution. And then go back and finish off your version.
And once you’re done? Then click on through to put the final touches on your contact list. And after that, we’ll discuss what comes after this course.