Managing State in React With Unstated

As your application becomes more complex, the management of state can become tedious. A component’s state is meant to be self-contained, which makes sharing state across multiple components a headache. Redux is usually the go-to library to manage state in React, however, depending on how complex your application is, you might not need Redux.

Unstated is an alternative that provides you with the functionality to manage state across multiple components with a Container class and Provider and Subscribe components. Let’s see Unstated in action by creating a simple counter and then look at a more advanced to-do application.

Using Unstated to Create a Counter

The code for the counter we’re making is available on GitHub:

View Repo

You can add Unstated to your application with Yarn:

yarn add unstated

Container

The container extends Unstated’s Container class. It is to be used only for state management. This is where the initial state will be initialized and the call to setState() will happen.

import { Container } from 'unstated' class CounterContainer extends Container { state = { count: 0 } increment = () => { this.setState({ count: this.state.count + 1 }) } decrement = () => { this.setState({ count: this.state.count - 1 }) }
} export default CounterContainer

So far, we’ve defined the Container (CounterContainer), set its starting state for count at the number zero and defined methods for adding and subtracting to the component’s state in increments and decrements of one.

You might be wondering why we haven’t imported React at this point. There is no need to import it into the Container since we will not be rendering JSX at all.

Events emitters will be used in order to call setState() and cause the components to re-render. The components that will make use of this container will have to subscribe to it.

Subscribe

The Subscribe component is used to plug the state into the components that need it. From here, we will be able to call the increment and decrement methods, which will update the state of the application and cause the subscribed component to re-render with the correct count. These methods will be triggered by a couple of buttons that contain events listeners to add or subtract to the count, respectively.

import React from 'react'
import { Subscribe } from 'unstated' import CounterContainer from './containers/counter' const Counter = () => { return ( <Subscribe to={[CounterContainer]}> {counterContainer => ( <div> <div> // The current count value Count: { counterContainer.state.count } </div> // This button will add to the count <button onClick={counterContainer.increment}>Increment</button> // This button will subtract from the count <button onClick={counterContainer.decrement}>Decrement</button> </div> )} </Subscribe> )
} export default Counter

The Subscribe component is given the CounterContainer in the form of an array to its to prop. This means that the Subscribe component can subscribe to more than one container, and all of the containers are passed to the to prop of the Subscribe component in an array.

The counterContainer is a function that receives an instance of each container the Subscribe component subscribes to.

With that, we can now access the state and the methods made available in the container.

Provider

We’ll make use of the Provider component to store the container instances and allow the children to subscribe to it.

import React, { Component } from 'react';
import { Provider } from 'unstated' import Counter from './Counter' class App extends Component { render() { return ( <Provider> <Counter /> </Provider> ); }
} export default App;

With this, the Counter component can make use of our counterContainer.

Unstated allows you to make use of all the functionality that React’s setState() provides. For example, if we want to increment the previous state by one three times with one click, we can pass a function to setState() like this:

incrementBy3 = () => { this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 }))
}

The idea is that the setState() still works like it does, but this time with the ability to keep the state contained in a Container class. It becomes easy to spread the state to only the components that need it.

Let’s Make a To-Do Application!

This is a slightly more advanced use of Unstated. Two components will subscribe to the container, which will manage all of the state, and the methods for updating the state. Again, the code is available on Github:

View Repo

The container will look like this:

import { Container } from 'unstated' class TodoContainer extends Container { state = { todos: [ 'Mess around with unstated', 'Start dance class' ], todo: '' }; handleDeleteTodo = (todo) => { this.setState({ todos: this.state.todos.filter(c => c !== todo) }) } handleInputChange = (event) => { const todo = event.target.value this.setState({ todo }); }; handleAddTodo = (event) => { event.preventDefault() this.setState(({todos}) => ({ todos: todos.concat(this.state.todo) })) this.setState({ todo: '' }); } } export default TodoContainer

The container has an initial todos state which is an array with two items in it. To add to-do items, we have a todo state set to an empty string.

We’re going to need a CreateTodo component that will subscribe to the container. Each time a value is entered, the onChange event will trigger then fire the handleInputChange() method we have in the container. Clicking the submit button will trigger handleAddTodo(). The handleDeleteTodo() method receives a to-do and filters out the to-do that matches the one passed to it.

import React from 'react'
import { Subscribe } from 'unstated' import TodoContainer from './containers/todoContainer' const CreateTodo = () => { return ( <div> <Subscribe to={[TodoContainer]}> {todos => <div> <form onSubmit={todos.handleAddTodo}> <input type="text" value={todos.state.todo} onChange={todos.handleInputChange} /> <button>Submit</button> </form> </div> } </Subscribe> </div> );
} export default CreateTodo

When a new to-do is added, the todos state made available in the container is updated. The list of todos is pulled from the container to the Todos component, by subscribing the component to the container.

import React from 'react';
import { Subscribe } from 'unstated'; import TodoContainer from './containers/todoContainer' const Todos = () => ( <ul> <Subscribe to={[TodoContainer]}> {todos => todos.state.todos.map(todo => ( <li key={todo}> {todo} <button onClick={() => todos.handleDeleteTodo(todo)}>X</button> </li> )) } </Subscribe> </ul>
); export default Todos

This component loops through the array of to-dos available in the container and renders them in a list.

Finally, we need to wrap the components that subscribe to the container in a provider like we did in the case of the counter. We do this in our App.js file exactly like we did in the counter example:

import React, { Component } from 'react';
import { Provider } from 'unstated' import CreateTodo from './CreateTodo'
import Todos from './Todos' class App extends Component { render() { return ( <Provider> <CreateTodo /> <Todos /> </Provider> ); }
} export default App;

Wrapping Up

There are different ways of managing state in React depending on the complexity of your application and Unstated is a handy library that can make it easier. It’s worth reiterating the point that Redux, while awesome, is not always the best tool for the job, even though we often grab for it in these types of cases. Hopefully you now feel like you have a new tool in your belt.

The post Managing State in React With Unstated appeared first on CSS-Tricks.

Understanding React `setState`

React components can, and often do, have state. State can be anything, but think of things like whether a user is logged in or not and displaying the correct username based on which account is active. Or an array of blog posts. Or if a modal is open or not and which tab within it is active.

React components with state render reconciliation. The reconciliation process is the way React updates the DOM, by making changes to the component based on the change in state. When the request to setState() is triggered, React creates a new tree containing the reactive elements in the component (along with the updated state). This tree is used to figure out how the Search component’s setState Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Let’s register the component and define the markup for the Understanding React `setState` appeared first on CSS-Tricks.

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.