DEV Community 👩‍💻👨‍💻

𝐃𝐚𝐧𝐫𝐞𝐳
𝐃𝐚𝐧𝐫𝐞𝐳

Posted on

Learn How to create components, pass them data using props, and manage their data using state, by building a Contact Manager

Our Contact Manager will allow us to view the list of contacts and add new ones to the list.

Image description

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

Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

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>
);
Enter fullscreen mode Exit fullscreen mode

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>
);
}
Enter fullscreen mode Exit fullscreen mode

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]);
 }
....
}
Enter fullscreen mode Exit fullscreen mode

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>
);
}
Enter fullscreen mode Exit fullscreen mode

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>
);
}
Enter fullscreen mode Exit fullscreen mode

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)

Need better DEV posts?

You can set your approximate experience level in settings which can help improve the relevance of your DEV Home Feed.