React State From the Ground Up

Post pobrano z: React State From the Ground Up

As you begin to learn React, you will be faced with understanding what state is. State is hugely important in React, and perhaps a big reason you’ve looked into using React in the first place. Let’s take a stab at understanding what state is and how it works.

What is State?

State, in React, is a plain JavaScript object that allows you keep track of a component’s data. The state of a component can change. A change to the state of a component depends on the functionality of the application. Changes can be based on user response, new messages from server-side, network response, or anything.

Component state is expected to be private to the component and controlled by the same component. To make changes to a component’s state, you have to make them inside the component — the initialization and updating of the component’s state.

Class Components

States is only available to components that are called class components. The main reason why you will want to use class components over their counterpart, functional components, is that class components can have state. Let’s see the difference. Functional components are JavaScript functions, like this:

const App = (props) => {
  return (
    <div>
      { this.props }
    </div>
  )
}

If the functionality you need from your component is as simple as the one above, then a functional component is the perfect fit. A class component will look a lot more complex than that.

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = { username: 'johndoe' }
  }
  render() {
    const { username } = this.state
    return(
      <div>
        { username }
      </div>
    )
  }
}

Above, I am setting the state of the component’s username to a string.

The Constructor

According to the official documentation, the constructor is the right place to initialize state. Initializing state is done by setting this.state to an object, like you can see above. Remember: state is a plain JavaScript object. The initial state of the App component has been set to a state object which contains the key username, and its value johndoe using this.state = { username: 'johndoe' }.

Initializing a component state can get as complex as what you can see here:

constructor(props) {
  super(props)
  this.state = { 
    currentTime: 0,
    status: false, 
    btnOne: false, 
    todoList: [],
    name: 'John Doe'
  }
}

Accessing State

An initialized state can be accessed in the render() method, as I did above.

render() {
  const { username } = this.state
  return(
    <div>
      { username }
    </div>
  )
}

An alternative to the above snippet is:

render() {
  return(
    <div>
      { this.state.username }
    </div>
  )
}

The difference is that I extracted the username from state in the first example, but it can also be written as const status = this.state.username. Thanks to ES6 destructuring, I do not have to go that route. Do not get confused when you see things like this. It is important to know that I am not reassigning state when I did that. The initial setup of state was done in the constructor, and should not be done again – never update your component state directly.

A state can be accessed using this.state.property-name. Do not forget that aside from the point where you initialized your state, the next time you are to make use of this.state is when you want to access the state.

Updating State

The only permissible way to update a component’s state is by using setState(). Let’s see how this works practically.

First, I will start with creating the method that gets called to update the component’s username. This method should receive an argument, and it is expected to use that argument to update the state.

handleInputChange(username) {
  this.setState({username})
}

Once again, you can see that I am passing in an object to setState(). With that done, I will need to pass this function to the event handler that gets called when the value of an input box is changed. The event handler will give the context of the event that was triggered which makes it possible to obtain the value entered in the input box using event.target.value. This is the argument passed to handleInputChange() method. So, the render method should look like this.

render() {
  const { username } = this.state
  return (
    <div>
      <div>
        <input 
          type="text"
          value={this.state.username}
          onChange={event => this.handleInputChange(event.target.value)}
        />
      </div>
      <p>Your username is, {username}</p>
    </div>
  )
}

Each time setState() is called, a request is sent to React to update the DOM using the newly updated state. Having this mindset makes you understand that state update can be delayed.

Your component should look like this;

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = { username: 'johndoe' }
  }
  handleInputChange(username) {
    this.setState({username})
  }
  render() {
    const { username } = this.state
    return (
      <div>
        <div>
          <input 
            type="text"
            value={this.state.username}
            onChange={event => this.handleInputChange(event.target.value)}
          />
        </div>
        <p>Your username is, {username}</p>
      </div>
    )
  }
}

Passing State as Props

A state can be passed as props from a parent to the child component. To see this in action, let’s create a new component for creating a To Do List. This component will have an input field to enter daily tasks and the tasks will be passed as props to the child component.

Try to create the parent component on your own, using the lessons you have learned thus far.

Let’s start with creating the initial state of the component.

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = { todoList: [] }
  }
  render() {
    return()
  }
}

The component’s state has its todoList set to an empty array. In the render() method, I want to return a form for submitting tasks.

render() {
  const { todoList } = this.state
  return (
    <div>
      <h2>Enter your to-do</h2>
      <form onSubmit={this.handleSubmit}>
        <label>Todo Item</label>
        <input
          type="text"
          name="todoitem"
        />
        <button type="submit">Submit</button>
      </form>
    </div >
  )
}

Each time a new item is entered and the submit button is clicked, the method handleSubmit gets called. This method will be used to update the state of the component. The way I want to update it is by using concat to add the new value in the todoList array. Doing so will set the value for todoList inside the setState() method. Here’s how that should look:

handleSubmit = (event) => {
  event.preventDefault()
  const value = (event.target.elements.todoitem.value)
  this.setState(({todoList}) => ({
    todoList: todoList.concat(value)
  }))
}

The event context is obtained each time the submit button is clicked. We use event.preventDefault() to stop the default action of submission which would reload the page. The value entered in the input field is assigned a variable called value, which is then passed an argument when todoList.concat() is called. React updates the state of todoList by adding the new value to the initial empty array. This new array becomes the current state of todoList. When another item is added, the cycle repeats.

A chart illustrating the cycle explained above.

The goal here is to pass the individual item to a child component as props. For this tutorial, we’ll call it the TodoItem component. Add the code snippet below inside the parent div which you have in render() method.

<div>
  <h2>Your todo lists include:</h2>
  { todoList.map(i => <TodoItem item={i} /> )}
</div>

You’re using map to loop through the todoList array, which means the individual item is then passed to the TodoItem component as props. To make use of this, you need to have a TodoItem component that receives props and renders it on the DOM. I will show you how to do this using functional and class components.

Written as a functional component:

const TodoItem = (props) => {
  return (
    <div>
      {props.item}
    </div>
  )
}

For the class component, it would be:

class TodoItem extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    const {item} = this.props
    return (
      <div>
        {item}
      </div>
    )
  }
}

If there is no need to manage state in this component, you are better off using functional component.

Leveling Up

You will be handling state very often while developing React application. With all the areas covered above, you should have the confidence of being able to dive into the advanced part of state management in React. To dig deeper, I recommend React’s official documentation on State and Lifecycle as well as Uber’s React Guide on Props vs State.

The post React State From the Ground Up appeared first on CSS-Tricks.

Dodaj komentarz