Our Contact Manager will allow us to view the list of contacts and add new ones to the list.
By looking at the mockup, it makes sense to have two-component
AddPersonForm: a form with the text field and Add button.
PeopleList:a list of contacts
Let's create these components.
AddPersonForm uses the state to manage the value of the text field:
function AddPersonForm() {
const [person, setPerson] = useState("");
}
function handleChange(e) {
setPerson(e.target.value);
}
function handleSubmit(e) {
e.preventDefault();
}
return (
<form onSubmit = {handleSubmit}>
<input type="text"
placeholder="Add new contact"
onChange={handleChange}
value={person} />
<button type="submit">Add</button>
</form>
);
}
For now, we just prevent the default behavior when the form is submitted.
PeopleList received an array representing the contacts and renders a list on the page:
function PeopleList(props) {
const arr = props.data;
const listItems = arr.map((val, index) =>
<li Key={index}>{val}</li>
);
return <ul>{listItems}</ul>;
}
Now we can render our components on the page and include some initial data:
const contacts = ["James Smith", "Thomas Anderson", "Bruce Wayne"];
const el = (
<div>
<AddPersonForm />
<PeopleList data={contacts} />
</div>
);
Sharing State
Right now, our AddPersonForm independently keeps its state.
How can we add a new contact to our PeopleList when the form is submitted?
To accomplish that, we need to share the state between the components. We can do that by lifting the state to a parent component. This means that the parent component will hold the data that needs to be shared between the components. in our case, that is the contacts list.
Let's create a parent component called ContactManager, which includes the AddPersonForm and PersonList as child components, and hold the contacts list in its state:
function ContactManager(props) {
const [contacts, setContacts] =
useState(props.data);
return (
<div>
<AddPersonForm />
<PeopleList data={contacts} />
</div>
);
}
Adding a Contact
Now, we can create an addPerson() function to our ContactManager component to add a new person to our contacts state array:
function ContactManager(props) {
const [contacts, setContacts] =
useState(props.data);
function addPerson(name) {
setContacts([...contacts, name]);
}
....
}
But how are we going to call this function from our child AddPersonForm component, where the data for the new person is stored?
Just like we passed down data using props, React allows us to pass down function references!
function ContactManager(props) {
const [contacts, setContacts] =
useState(props.data);
function addPerson(name) {
setContacts([...contacts, name]);
}
return (
<div>
<AddPersonForm handleSubmit={addPerson} />
<PeopleList data={contacts} />
</div>
);
}
Similar to passing the contacts list to our PeopleList component,
we passed down the addPerson() function to our AddpersonForm using a prop called handleSubmit.
Now, our PeopleList can call the handleSubmit function that it received when the form is submitted, to add a new person to the list:
function AddPersonForm(props) {
const [person, setPerson] = useState('');
function handleChange(e) {
setPerson(e.target.value);
}
function handleSubmit(e) {
props.handleSubmit(person);
setPerson('');
e.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<input type="text"
placeholder="Add new contact"
onChange={handleChange}
value={person} />
<button type="submit">Add</button>
</form>
);
}
Conclusion
An important takeaway from this lesson is that props can be used to pass down not only the state but also functions, that may manipulate the state.
This way, we can store the application state in the parent and allow its child components to use and manipulate the state.
Now, when our app is a full function, we can add some CSS styles and a check, to prevent the creation of blank contacts.
Top comments (0)