React State

Tutorials / JavaScript Tutorials / React State

React State

tutorial javascript react

Now you know how to use React and JSX to build a page from nested components.

So far, all of the examples have used hard-coded content that never changes. This tutorial introduces state, which lets you handle changing data that’s rendered to the page.

Events

Talking about data changing is a little easier if we can get input from the user, so before we talk about state, let’s talk about events.

This example shows a button that increments a count whenever the user clicks.

Note: I’m using this example to talk about events, but you shouldn’t actually track the count like this! Read on to find out why.

There are a couple new things here:

First, the component contains a constructor:

constructor(props) {
  super(props);
  this.count = 0;
  this.showMessage = this.showMessage.bind(this);
}

The constructor takes a props argument, which it passes to the parent class’s constructor using the super keyword. If your component contains a constructor, you need this- even if your component doesn’t use any properties!

Then the constructor initializes a count variable, and then contains this line:

this.showMessage = this.showMessage.bind(this);

This goofy-looking line is needed to make the showMessage() function work. Specifically, it’s needed to make the this keyword work inside the showMessage() function. Check out React’s events tutorial for more info on why.

The other new syntax is this JSX in the render() function:

<button onClick={this.showMessage}>
  Click
</button>

This JSX creates a React button element that will call the showMessage() function whenever the user clicks.

The Wrong Way

The above example contains data that changes over time: the click variable increments whenever the user clicks the button.

Right now, the click is only shown in an alert dialog. What if you wanted to show it directly in the page, in the HTML that’s created by the render() function?

You might try adding this line to the JSX, just under the closing </button> tag:

<p>You clicked {this.count} times.</p>

If you do this, you’ll see You clicked 0 times. rendered to the page under the button. But if you click the button, the count displayed in the page never increases!

That’s because React doesn’t know that it needs to re-render the component. You might try calling render() yourself, but that won’t work either because you aren’t doing anything with the returned element.

To fix this, you can use something that React calls state.

The Right Way

To tell React that a piece of data is tied to what’s rendered by a component, you need to do three things:

First, in your component’s constructor, initialize the state variable, and set it equal to an object that contains the fields you want to track.

Second, use the state variable inside your render() function:

<p>You clicked {this.state.count} times.</p>

Finally, whenever you want to change a piece of data, call the setState() function:

this.setState({count: this.state.count + 1});

Note: For React to see your change, you must call the setState() function! In this example, incrementing the this.state.count variable directly with the ++ operator would not work!

Another note while we’re at it: When you call setState(), you only need to include variables that have changed. The object you pass in will be merged with your current state.

Putting it all together, it looks like this:

Nested Stateful Components

Now you know how to use state in your components to change what’s shown in the DOM, and you’ve already seen how to nest your components to create more complicated DOM structures.

You can combine those ideas to create a page that consists of multiple nested stateful components.

Here’s an updated version of the todo list example:

This code defines a top-level App component that renders a heading and a TaskList. The TaskList component renders a list that contains three Task elements. The Task component takes a label property and tracks a done boolean in its state. Clicks the task toggles its done field, which is used to render Done or Not done in the DOM.

Coming soon: How to create feedback loops from the top level of your React app, down to individual components, and back again!

Comments and Questions

Happy Coding is a community of folks just like you learning about coding. Do you have a comment or question? Post it here!


Read the full article on Happy Coding at https://happycoding.io/tutorials/javascript/react-state Replies to this post will show as comments on the original article!

Inside my react Component, I was using/rendering another react sub-component. I was having difficulty putting onClick event on this react Sub-component directly. Is there a way to do so??

I want to call the Tasklist sub-component directly from its parents in such a manner that I don’t need to put onClick on the li tag present inside the Tasklist sub-component.
image

Can you post your full code, or a link to a CodePen?

But if I’m understanding your question correctly, you’re saying that you want to add an onClick attribute to your TaskList but not on your li element.

If that’s your goal, the thing to keep in mind is that React elements like TaskList are not directly added to the DOM. If you want the onClick handler to be in the HTML that’s added to the DOM, it needs to eventually be on an HTML element. In your case, that’s the <li> tag. Alternatively, you might be able to add it to the <ul> tag instead?

But in any case, you need to add it to some HTML element, because that’s what ends up being added to the DOM.

Let me know if that makes sense!

1 Like

Yeah, this is exactly what I wanted to know.

Thanks, I got it now.

1 Like

On this scrimba page if I paste the below code inside index.js and run it, then I get the desired output in the console, i.e. thingsArray is appended with Thing 3, Thing 4, … , Thing n, everytime I click the “add item” button.


import React from ‘react’;
import ReactDOM from ‘react-dom’;

function App() {
let thingsArray = [“Thing 1”,“Thing 2”]
const [state, setState] = React.useState([“Thing 1”, “Thing 2”])

function addItem() {
    thingsArray.push(`Thing ${thingsArray.length+1}`)
    console.log(thingsArray)
    // setState([...thingsArray])
}

const thingsElements = state.map(thing => <p key={thing}>{thing}</p>)
return (
    <div>
        <button onClick={addItem}>Add Item</button>
        {thingsElements}
    </div>
)

}

ReactDOM.render(, document.getElementById(‘root’));


But when I try to change the state of the component (i.e. if I uncomment the line 11 - the setState() one), then I don’t get the desired result. I don’t know why but the thingsArray remains = [“Thing 1”, “Thing 2”, “Thing 3”] , no matter how many times I click the “add item” button.

pls help. I have given several hours to understand this, but to no avail.

PROBABLE ANSWER:
Maybe I got this now. Every time a state is changed, the whole component is re-run together with the child components. Because of this rerunning, the “thingsArrray” variable was also getting reinitialized everytime and that is why I was not getting the desired output.

Hey sorry for the delay, I’ve been travelling this week. Greetings from kinda-sunny southern California!

I think you’re on the right track. In React, everything is based on the state- your local variables don’t rrreally matter, unless you add them to your state.

Right now your code actually creates two arrays:

let thingsArray = [“Thing 1”,“Thing 2”]
const [state, setState] = React.useState([“Thing 1”, “Thing 2”])

Then your addItem() function modifies the thingsArray array, but your function builds the page based on the state array.

In other words, you’re modifying a different array than what’s in your state!

To fix this, you probably want to only use a single array:

import React from 'react';
import ReactDOM from 'react-dom';

function App() {
const [state, setState] = React.useState(["Thing 1", "Thing 2"])

function addItem() {
    const thingsArray = state;
    const newThingsArray = thingsArray.concat(`Thing ${thingsArray.length+1}`);
    console.log(newThingsArray);
    setState([...newThingsArray]);
}

const thingsElements = state.map(thing => <p key={thing}>{thing}</p>)
return (
    <div>
        <button onClick={addItem}>Add Item</button>
        {thingsElements}
    </div>
)
}

ReactDOM.render(<App />, document.getElementById('root'));

Doing it this way seems to work, but from the React I’ve seen, I’d also maybe expect your state to be an object that contains fields instead of an array. One of the fields in that state could be your array though, something like this:

import React from 'react';
import ReactDOM from 'react-dom';

function App() {
const [state, setState] = React.useState({thingsArray: ["Thing 1", "Thing 2"]})

function addItem() {
    const newThingsArray = state.thingsArray.concat(`Thing ${state.thingsArray.length+1}`);
    console.log(newThingsArray);
    setState({thingsArray: newThingsArray});
}

const thingsElements = state.thingsArray.map(thing => <p key={thing}>{thing}</p>)
return (
    <div>
        <button onClick={addItem}>Add Item</button>
        {thingsElements}
    </div>
)
}

ReactDOM.render(<App />, document.getElementById('root'));

Honestly I’m not sure which is more idiomatic though, so I’d be curious if you’ve seen it done differently.

1 Like

Cooool. I also wish to travel a lot once I get a job :hugs:. safe travels :v:t3:.
Thanks, I got it now.
Using an object as a state feels more natural to me.

1 Like