Updated April 20, 2023
Introduction to React State Management
The state of any application is represented by the user interface of the application. The state is mutable and whenever the user interacts with the application and changes its state, the user interface of the app may change as it will be represented by the new state. These states can be managed by a React component. The main objectives of the react component are to store the state and allow it to get updated once the user interacts with the application. It also ensures that the UI change whenever there is any update in the State. In this article, we will explain the ways to manage the states. In this topic, we are going to learn about React State Management.
Managing State in React with Examples
Here are the following examples mention below:
Example #1 – React State: useState and React State: useContext
useState hook in React is used by many React beginners when they first start using state in React.
The initial state is taken as an argument in useState hook. Initially when the React component renders, and returns two values. The values are the state update function and the current state. For displaying the current state of the component current state is used and for changing the current state the state update function is used.
useContext helps in passing the props down the components tree. React’s Context API helps in passing the props between the grandfather component to the grandchild component. This process doesn’t bother the other React Components which are available in the chain.
In the example below, the value can be changed in the value changing window and we can toggle between different themes as well with the mentioned buttons.
The files used to develop the application are mentioned in the below image, followed by the code of each file respectively.
defaults.js
export const themes = {
light: {
foreground: "#b440de",
background: "#f05983",
color: "#f2f7fa"
},
dark: {
foreground: "#bcf55b",
background: "#de81fc",
color: "black"
}
};
const testValue = "Other Value";
const defaultContext = {
theme: themes.light,
toggleTheme: () => {},
value: testValue,
setValue: () => {}
};
export default defaultContext;
App.js
import React
, { useContext } from "react";
import AppContext from "./AppContext";
import ChildComponentOne from "./ChildComponentOne";
import ChangeInputValue from "./ChangeInputValue";
const App = props => {
const { theme, toggleTheme } = useContext(AppContext);
return (
<div className="App">
<h1>Example of useContext & useState</h1>
<div className="flex-container">
<div className="container" style={{ ...theme }}>
Content 1
</div>
<ChildComponentOne />
<div className="container" style={{ ...theme }}>
Content 2
</div>
</div>
<h2>Unicorn Mode</h2>
<button onClick={toggleTheme}>Switch for Unicorn Mode 2</button>
<ChangeInputValue />
</div>
);
};
export default App;
AppContext.js
import { createContext } from "react";
import defaultContext from "./context/defaults";
const AppContext = createContext(defaultContext);
export default AppContext;
ChangeInputValue.js
import React
, { useContext } from "react";
import AppContext from "./AppContext";
const ChangeInputValue = props => {
const { value, setValue } = useContext(AppContext);
const updateValue = () => {
const newValue = document.querySelector(".newValue").value;
setValue(newValue);
document.querySelector(".newValue").value = "";
};
return (
<>
<h2>Change Value Here</h2>
<input className="newValue" placeholder="enter new value..." />
<button onClick={updateValue}>Click to Set Value</button>
<p>Value is = "{value}"</p>
</>
);
};
export default ChangeInputValue;
ChildComponentOne.js
import React
, { useContext } from "react";
import AppContext from "./AppContext";
import ChildComponentTwo from "./ChildComponentTwo";
const ChildComponentOne = props => {
const { theme } = useContext(AppContext);
return (
<div>
<p style={theme}>Component One</p>
<ChildComponentTwo />
</div>
);
};
export default ChildComponentOne;
ChildComponentTwo.js
import React
, { useContext } from "react";
import AppContext from "./AppContext";
const ChildComponentTwo = props => {
const { theme, toggleTheme, value } = useContext(AppContext);
return (
<div>
<p style={{ ...theme }}>Component Two</p>
<p>value = "{value}"</p>
<button onClick={toggleTheme}>Switch for Unicorn Mode 1</button>
</div>
);
};
export default ChildComponentTwo;
index.js
import React
, { useContext
, useState } from "react";
import ReactDOM from "react-dom";
import App from "./App";
import AppContext from "./AppContext";
import { themes } from "./context/defaults";
import "./styles.css";
function Root() {
const appContext = useContext(AppContext);
const [context, setContext] = useState(appContext);
const [isDarkMode, setDarkMode] = useState(false);
const toggleTheme = () => {
setContext({ ...context, theme: isDarkMode ? themes.light : themes.dark });
setDarkMode(!isDarkMode);
};
const setValue = value => {
setContext({ ...context, value });
};
const state = {
...context,
toggleTheme: toggleTheme,
setValue: setValue
};
return (
<AppContext.Provider value={state}>
<App />
</AppContext.Provider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Root />, rootElement);
styles.css
.App {
font-family: 'Times New Roman'
, Times
, serif;
text-align: center;
}
.flex-container {
display: flex;
justify-content: center;
}
.container {
border-radius: 50%;
height: 50px;
width: 100px;
margin: 25px;
border: 1px solid #de338e;
display: flex;
justify-content: center;
align-items: center;
}
Output:
Example #2 – React State: useReducer
The idea of React’s useReducer has been taken from JavaScript Reducer. Generally, the current state is held by the Reducer along with action with payload and then it results out the new state.
In the example below, we have developed a counter, and its value state is maintained with the help of useReducer. We can increase, decrease, and reset the value of the counter.
The files used to develop the counter are mentioned in the below image, followed by the code of each file respectively below it.
Counter.js
import React
, { useReducer } from "react"
function init(initialCount) {
while (initialCount < 0) {
initialCount++
}
return { count: initialCount }
}
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count
: state.count + 1 }
case "decrement":
return { count
: state.count - 1 }
case "reset":
return init(action.payload)
default:
throw new Error()
}
}
function Counter({ initialCount }) {
const [state
, dispatch] = useReducer(reducer
, initialCount
, init)
return (
<>
Count: {state.count}
<br />
<button
onClick={() => dispatch({ type: "reset", payload: initialCount })}
>
Reset Value
</button>
<button onClick={() => dispatch({ type: "increment" })}>Increasing Button</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decreasing Button</button>
</>
)
}
export default Counter
index.js
import React from "react";
import ReactDOM from "react-dom";
import Counter from "./Counter";
function App() {
return (
<div className="App">
<Counter initialCount={0} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Output:
Example #3 – React State: Redux
With Redux, the state can be managed globally by the use of an external force. The Redux Reducer’s work is to act upon two Redux actions and there is no dependency on the Redux Library.
In the example below, we have developed a ToDo app, were state is maintained with the help of Redux.
The files used to develop the ToDO app are mentioned in the below image, followed by the code of each file respectively below it.
index.html
<!DOCTYPE html>
<html>
<head>
<title>React State Management Using Redux</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>
React State Management Using Redux
</h1>
<div id="app">
<div class="loading">
Page is Loading...
</div>
</div>
</body>
</html>
Input.js
import * as React from "react";
class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleKeyPress(e) {
if (e.key === "Enter") {
this.props.onCreate(this.state.value);
this.setState({
value: ""
});
}
}
render() {
return (
<input
placeholder="Enter Your Tasks Here"
onKeyPress={this.handleKeyPress}
onChange={this.handleChange}
value={this.state.value}
/>
);
}
}
export default Input;
Item.js
import * as React from "react";
class Item extends React.Component {
constructor(props) {
super(props);
this.handleToggle = this.handleToggle.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);
}
handleToggle(event) {
this.setState({ value: event.target.value });
}
handleKeyPress(e) {
if (e.key === "Enter") {
this.props.onCreate(this.state.value);
this.setState({
value: ""
});
}
}
handleOnChange(e) {
this.props.onClick(this.props.id, e.target.checked);
}
render() {
return (
<div>
<input
id={`checkbox-${this.props.id}`}
type="checkbox"
value={this.props.done ? "false" : "true"}
onChange={this.handleOnChange}
checked={this.props.done}
/>
<label htmlFor={`checkbox-${this.props.id}`}>{this.props.text}</label>
</div>
);
}
}
export default Item;
List.js
import Item from "./Item";
import * as React from "react";
class List extends React.Component {
render() {
if (this.props.items.length === 0) {
return null;
}
return (
<div>
<strong>{this.props.title}</strong>
{this.props.items.map(item => (
<Item
key={item.id}
id={item.id}
text={item.text}
done={item.done}
onClick={this.props.onItemClick}
/>
))}
</div>
);
}
}
export default List;
actions.js
import { ADD_TODO
, MARK_TODO_DONE
, MARK_TODO_NOT_DONE } from "./constants";
export const addTodo = text => (
{
type: ADD_TODO
, text
}
);
export const markTodoDone = id => ({ type: MARK_TODO_DONE, id });
export const markTodoNotDone = id => ({
type: MARK_TODO_NOT_DONE,
id
});
constants.js
export const ADD_TODO = "ADD_TODO";
export const MARK_TODO_DONE = "MARK_TODO_DONE";
export const MARK_TODO_NOT_DONE = "MARK_TODO_NOT_DONE";
index.js
import * as React from "react";
import * as ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider
, connect } from "react-redux";
import Input from "./components/Input";
import List from "./components/List";
import { addTodo
, markTodoDone
, markTodoNotDone } from "./actions";
import reducer from "./reducers";
class App extends React.Component {
constructor(props) {
super(props);
this.handleCreate = this.handleCreate.bind(this);
this.itemClick = this.itemClick.bind(this);
}
handleCreate(text) {
this.props.addTodo(text);
}
itemClick(id, done) {
if (done) {
this.props.markTodoDone(id);
} else {
this.props.markTodoNotDone(id);
}
}
render() {
return (
<div>
<Input onCreate={this.handleCreate} />
<List
title="Tasks to be Done"
items={this.props.items.filter(item => !item.done)}
onItemClick={this.itemClick}
/>
<List
title="Completed Tasks"
items={this.props.items.filter(item => item.done)}
onItemClick={this.itemClick}
/>
</div>
);
}
}
const mapStateToProps = state => ({
items: state.items
});
const mapDispatchToProps = dispatch => ({
addTodo: text => dispatch(addTodo(text)),
markTodoDone: id => dispatch(markTodoDone(id)),
markTodoNotDone: id => dispatch(markTodoNotDone(id))
});
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById("app")
);
reducers.js
import { ADD_TODO
, MARK_TODO_DONE
, MARK_TODO_NOT_DONE } from "./constants";
const initialState = {
items: []
};
export default function reducer(
state = initialState
, action
) {
switch (action.type) {
case ADD_TODO:
const maxId = state.items.reduce(
(maxId, todo) => Math.max(todo.id, maxId),
-1
);
return {
...state,
items: state.items.concat({
id: maxId + 1,
done: false,
text: action.text
})
};
case MARK_TODO_DONE:
return {
...state,
items: state.items.map(item => ({
id: item.id,
done: item.id === action.id ? true : item.done,
text: item.text
}))
};
case MARK_TODO_NOT_DONE:
return {
...state,
items: state.items.map(item => ({
id: item.id,
done: item.id === action.id ? false : item.done,
text: item.text
}))
};
default:
return state;
}
}
Output:
Conclusion
On the basis of the above article, we understood the concept of State Management and the different ways to achieve it. The above examples introduce you to the different ways of managing the state and helps in understanding the working of each one of them.
Recommended Articles
This is a guide to React State Management. Here we discuss the ways to manage the States in React along with respective examples for better understanding. You may also have a look at the following articles to learn more –