React Props & State: Exercise 2 – State

This is the second exercise in the series. You may want to start with the first one.

React is a JavaScript library for building dynamic websites. It works by reacting to changes in the data, and making changes in what the user sees based on those changes. The data it reacts to are called props and state.

Props – Input data to the component

State – Internal state data of the component

Set Up

If you haven’t already, you’ll need to get the exercise files. If you already got them when you did the first exercise, you don’t need to get them again.

To get them, clone this repository to your computer:
https://github.com/natafaye/props-and-state-practice

You can clone it using git with this command:

git clone https://github.com/natafaye/props-and-state-practice

Open the props-and-state-practice folder in Visual Studio Code.

Open a terminal in this folder (Ctrl + ` or Terminal > New Terminal)

Install the dependencies that are in the package.json file, with this command:

npm install

Exercise 2: Class Components

Expand the src folder, then the exercise-2-class folder.

In this folder there are three class components, the App component, the Contact component, and the ContactList component. You can mix functional and class components, but for this exercise we’ll use all class components to practice with them.

There is also a data.js file that holds some static data that we will use. In a real app, we might pull our data from a server, but for this exercise we’ll just pull it from the data.js file.

Set Up

We need to make sure our index.js is using the App component in this folder. Open index.js in the src folder, and make sure that the import for Exercise 2: Class Component is uncommented:

// Exercise 2: Class Components
import App from './exercise-2-class/App';

All the other App imports should be commented out.

The Goal

In Exercise 1 we set up our Contact component to show data about a contact.

Now we want our app to actually maintain some internal state data (a list of contacts) and show that data using our Contact List component, which uses the Contact component.

Our App should react to any changes in the list of contacts and re-render, which will re-render the ContactList component and the Contact components inside it.

The list of contacts should start out set to the array in the variable INITIAL_CONTACTS in the data.js file.

So, when it’s all completed, our app should look like this:

Tips

The Contact component is identical to the one in Exercise 1. You can copy your code from Contact.js in the exercise-1-class folder, into Contact.js in the exercise-2-class folder.

We will store our contacts in the state of the App component. Our ContactList and Contact components are purely presentational. Presentational means they only know how to present the data, they don’t manage the data or have any data in state. They just react to changes in their props, which will flow down to ContactList from App (and to Contact from ContactList).

React needs to be able to tell exactly what changed when it rerenders your component. Any component that displays a list needs to put a key attribute on the top JSX element (component or HTML element) of each item. The key should be set to something that is unique to each item in the list. It’s most often an ID.

Hints

How do I store the contacts in the state of the App component?

To add state to a class component, you need to create a constructor. Inside that constructor you can initialize the state. It might look something like this:

constructor(props) {
    super(props);
    this.state = {
        nameOfStateVariable: initialValueOfStateVariable
    }
}

Let’s break that down.

super(props);

You need this line first in any constructor for a class component. It calls the constructor of the Component class, which every class component inherits from. The constructor in the Component class sets up the props, among other things.

this.state = {
    nameOfStateVariable: initialValueOfStateVariable
}

This initializes our state. If we wanted to put a list of todos in our state, and we wanted to start out as an empty array, that might look like this:

this.state = {
     todos: []
}

We can put as many variables in our state as we want.

this.state = {
    todos: [],
    showDescription: true,
    selectedTodoId: 25
}

How do I access the list of contacts I stored in the state of the App component?

In a class component, you can access any state variable using:

this.state.nameOfStateVariable

How do I get the list of contacts from the App component to my ContactList component?

If you have data in a parent component that you need in a child component, you can pass it as a prop to that component.

In the parent component, you pass props to a child component like this:

<ChildComponent nameOfProp={dataToPutInTheProp} />

How do I use the list of contacts in the ContactList component?

In a class component, you can access any prop in the render() method using

this.props.nameOfProp

If you passed an object in as the data to put in the prop, you can access properties on that object like this:

this.props.nameOfProp.propertyName

Okay, I have the list of contacts. How do I use that list to show each contact with the Contact component?

We need to loop over the list of contacts and create a Contact component for each one.

You could use a for loop, and push each Contact to an array.

You could also use the .map() array method. This is a very common way, because it’s efficient and easy. It will create a new array of components from the array of data. It might look something like this:

arrayOfData.map( dataItem => <NameOfComponent propNameForDataItem={dataItem} key={dataItem.uniqueProperty} /> )

This will spit out an array of components. React knows how to show an array, so we can just put it straight in our JSX.

Maybe like this:

return (
    <div>
        { arrayOfData.map( dataItem => <NameOfComponent propName={dataItem} key={dataItem.uniqueProperty} /> ) }
    </div>
)

How many props & state variables should I use here?

I used one state variable in the App component, one prop in the ContactList component, and one prop in the Contact component.

I need more help. What might it look like to save the list of contacts in the state of the App component?

I saved it in a state variable called contacts. So my constructor looked like this:

constructor(props) {
    super(props);
    this.state = {
        contacts: INITIAL_CONTACTS
    }
}

I need more help. What might it look like to get the list of contacts to the ContactList component?

I saved the list of contacts in the a state variable called contacts. I passed the list of contacts to the ContactList component using a prop called contacts. It looked like this:

<ContactList contacts={this.state.contacts} />

I need more help. How do I set up the ContactList component to use the Contact component?

I passed the list of contacts to the ContactList component using a prop called contacts. I named the prop for the Contact component contact. So it looked like this:

<div className="row">
    { this.props.contacts.map( c => <Contact contact={c} key={c._id} /> ) }
</div>

Exercise 2: Functional Components

Expand the src folder, then the exercise-2-functional folder.

In this folder there are three functional components, the App component, the Contact component, and the ContactList component. You can mix functional and class components, but for this exercise we’ll use all functional components to practice with them.

There is also a data.js file that holds some static data that we will use. In a real app, we might pull our data from a server, but for this exercise we’ll just pull it from the data.js file.

Set Up

We need to make sure our index.js is using the App component in this folder. Open index.js in the src folder, and make sure that the import for Exercise 2: Functional Component is uncommented:

// Exercise 2: Functional Components
import App from './exercise-2-functional/App';

All the other App imports should be commented out.

The Goal

In Exercise 1 we set up our Contact component to show data about a contact.

Now we want our app to actually maintain some internal state data (a list of contacts) and show that data using our Contact List component, which uses the Contact component.

Our App should react to any changes in the list of contacts and re-render, which will re-render the ContactList component and the Contact components inside it.

The list of contacts should start out set to the array in the variable INITIAL_CONTACTS in the data.js file.

So, when it’s all completed, our app should look like this:

Tips

The Contact component is identical to the one in Exercise 1. You can copy your code from Contact.js in the exercise-1-functional folder, into Contact.js in the exercise-2-functional folder.

We will store our contacts in the state of the App component. Our ContactList and Contact components are purely presentational. Presentational means they only know how to present the data, they don’t manage the data or have any data in state. They just react to changes in their props, which will flow down to ContactList from App (and to Contact from ContactList).

Hints

How do I store the contacts in the state of the App component?

To add state to a functional component, you use the useState() hook.

export default function Component(props) {
    const [nameOfStateVariable, setNameOfStateVariable] = useState(initialValueOfStateVariable)

    // more code would be here
}

Let’s break that down.

This code uses object destructuring to get the (1) current value of the state variable and (2) the function for setting the state variable, and save them both in variables. We could do the same thing without object destructuring like this:

const currentValueAndSettingFunction = useState(initialValueOfStateVariable)
const nameOfStateVariable = currentValueAndSettingFunction[0];
const setNameOfStateVariable = currentValueAndSettingFunction[1];

nameOfStateVariable and setNameOfStateVariable are variable names that you choose. Best practice says to call the second one set + the name of the first one.

You can put as many variables in your state as you want:

export default function Component(props) {
    const [todos, setTodos] = useState( [] );
    const [showDescription, setShowDescription] = useState(true);
    const [selectedTodoId, setSelectedTodoId] = useState(25);
}

How do I access the list of contacts I stored in the state of the App component?

In a functional component, you can access any state variable using:

nameOfStateVariable

How do I get the list of contacts from the App component to my ContactList component?

If you have data in a parent component that you need in a child component, you can pass it as a prop to that component.

In the parent component, you pass props to a child component like this:

<ChildComponent nameOfProp={dataToPutInTheProp} />

How do I use the list of contacts in the ContactList component?

In a functional component, you can access any prop in the render() method using:

props.nameOfProp

If you passed an object in as the data to put in the prop, you can access properties on that object using:

props.nameOfProp.propertyName

If you used object destructuring when you passed the prop, you can access the prop using:

nameOfProp

And you can access properties on an object in a prop using:

nameOfProp.propertyName

Okay, I have the list of contacts. How do I use that list to show each contact with the Contact component?

We need to loop over the list of contacts and create a Contact component for each one.

You could use a for loop, and push each Contact to an array.

You could also use the .map() array method. This is a very common way because it’s efficient and easy. It will create a new array of components from the array of data. It might look something like this:

arrayOfData.map( dataItem => <NameOfComponent propName={dataItem} key={dataItem.uniqueProperty} /> )

This will spit out an array of components. React knows how to show an array, so we can just put it straight in our JSX.

Maybe like this:

return (
    <div>
        { arrayOfData.map( dataItem => <NameOfComponent propName={dataItem} key={dataItem.uniqueProperty} /> ) }
    </div>
)

How many props & state variables should I use here?

I used one state variable in the App component, one prop in the ContactList component, and one prop in the Contact component.

I need more help. What might it look like to save the list of contacts in the state of the App component?

I saved it in a state variable called contacts. So the line where I used the useState() hook looked like this:

const [contacts, setContacts] = useState(INITIAL_CONTACTS)

I need more help. What might it look like to get the list of contacts to the ContactList component?

I saved the list of contacts in the a state variable called contacts. I passed the list of contacts to the ContactList component using a prop called contacts.

So it looked like this:

<ContactList contacts={contacts} />

I need more help. How do I set up the ContactList component to use the Contact component?

I passed the list of contacts to the ContactList component using a prop called contacts. I named the prop for the Contact component contact. I used object destructuring on my props so instead of using props.contacts, I can just use contacts.

So it looked like this:

<div className="row">
    { contacts.map( c => <Contact contact={c} key={c._id} /> ) }
</div>

Conclusion

Were you able to get all the exercises working? Do you understand how to use props and state a little better?

Leave a Reply

Your email address will not be published. Required fields are marked *