Global state refers to data that is accessible across multiple components in a React application. Unlike the local state, which is confined to a single component, the global state can be accessed and modified from anywhere in the component tree.
In this article, we will explore the following approaches by which we can manage the global state in React.
Table of Content
Steps to Create React application and manage global state:
For React Context API: No additional installation is required, as it’s built into React.
Step 1: Create a React application using the following command:
npx create-react-app foldername
Step 2: After creating your project folder i.e. folder name, move to it using the following command:
cd foldername
Step 3: After setting up the React environment on your system, we can start by creating an App.js file and creating a directory by the name of components.
At last, we can apply any Approach to handle Global State
Folder Structure:
Approach 1: Using Redux
Include dependency in project:
npm install redux react-redux
The updated dependencies in package.json file will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"web-vitals": "^2.1.4"
}
Example: This example implements the above-mentioned approach.
// App.js import React, { useState } from 'react' ;
import { Provider } from 'react-redux' ;
import globalStateStore from './GlobalStateStore' ;
import GlobalStateDisplay from './GlobalStateDisplay' ;
const App = () => { const [inputValue, setInputValue] = useState( '' );
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
const updateGlobalState = () => {
const number = parseInt(inputValue);
if (!isNaN(number)) {
store.dispatch(
{ type: 'SET_COUNT' , payload: number }
);
}
};
return (
<Provider store={store}>
<div>
<input type= "number" value={inputValue}
onChange={handleInputChange} />
<button onClick={updateGlobalState}>
Update Global State</button>
<GlobalStateDisplay />
</div>
</Provider>
);
} export default App;
|
// GlobalStateDisplay.js import React from 'react' ;
import { useSelector } from 'react-redux' ;
const GlobalStateDisplay = () => { const count = useSelector(state => state.count);
const squaredValue = count ** 2;
return (
<div>
<p>Global State: {count}</p>
<p>Square of Global State:
{squaredValue}</p>
</div>
);
} export default GlobalStateDisplay;
|
//GlobalStateStore.js; import { createStore } from 'redux' ;
const initialState = { count: 0
}; const reducer = (state = initialState, action) => { switch (action.type) {
case 'SET_COUNT' :
return { ...state, count: action.payload };
default :
return state;
}
}; const GlobalStateStore = createStore(reducer); export default GlobalStateStore;
|
Approach 2: Using MobX
Include dependency in Project
npm install mobx mobx-react
The updated Dependencies in package.json file will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"mobx": "^6.12.0",
"mobx-react": "^9.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
Example: This example implements the above-mentioned approach.
// App.js import React, { useState } from 'react' ;
import { Provider } from 'mobx-react' ;
import globalStateStore from './GlobalStateStore' ;
import GlobalStateDisplay from './GlobalStateDisplay' ;
const App = () => { const [inputValue, setInputValue] = useState( '' );
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
const updateGlobalState = () => {
const number = parseInt(inputValue);
if (!isNaN(number)) {
globalStateStore.setCount(number);
}
};
return (
<Provider store={globalStateStore}>
<div>
<input type= "number" value={inputValue}
onChange={handleInputChange} />
<button onClick={updateGlobalState}>
Update Global State</button>
<GlobalStateDisplay />
</div>
</Provider>
);
} export default App;
|
// GlobalStateDisplay.js import React from 'react' ;
import { observer, inject } from 'mobx-react' ;
const GlobalStateDisplay = inject( 'store' )(
observer(({ store }) => {
const count = store.count;
const squaredValue = count ** 2;
return (
<div>
<p>Global State: {count}</p>
<p>Square of Global State: {squaredValue}</p>
</div>
);
})
); export default GlobalStateDisplay;
|
// GlobalStateStore.js import { makeAutoObservable } from 'mobx' ;
class GlobalStateStore { count = 0;
constructor() {
makeAutoObservable( this );
}
setCount(value) {
this .count = value;
}
get squaredValue() {
return this .count ** 2;
}
} const globalStateStore = new GlobalStateStore();
export default globalStateStore;
|
Approach 3: Using Recoil
Include dependency in Project
npm install recoil
The updated Dependencies in package.json file will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"recoil": "^0.7.7",
"web-vitals": "^2.1.4"
}
Example: This example implements the above-mentioned approach.
// App.js import React, { useState } from 'react' ;
import { useRecoilState } from 'recoil' ;
import { countState } from './GlobalStateStore' ;
import GlobalStateDisplay from './GlobalStateDisplay' ;
const App = () => { const [inputValue, setInputValue] = useState( '' );
const [count, setCount] = useRecoilState(countState);
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
const updateGlobalState = () => {
const number = parseInt(inputValue);
if (!isNaN(number)) {
setCount(number);
setInputValue( '' );
}
};
return (
<div>
<input type= "number" value={inputValue}
onChange={handleInputChange} />
<button onClick={updateGlobalState}>
Update Global State</button>
<GlobalStateDisplay />
</div>
);
}; export default App;
|
// GlobalStateDisplay.js import React from 'react' ;
import { useRecoilValue } from 'recoil' ;
import { countState } from './GlobalStateStore' ;
const GlobalStateDisplay = () => { const count = useRecoilValue(countState);
const squaredValue = count ** 2;
return (
<div>
<p>Global State:
{count}</p>
<p>Square of Global State:
{squaredValue}</p>
</div>
);
}; export default GlobalStateDisplay;
|
// GlobalStateStore.js import { atom } from 'recoil' ;
export const countState = atom({ key: 'countState' ,
default : 0,
}); |
/// index.js import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { RecoilRoot } from 'recoil' ;
ReactDOM.createRoot(document.getElementById( 'root' )).render(
<React.StrictMode>
<RecoilRoot>
<App />
</RecoilRoot>
</React.StrictMode>,
) |
Approach 4: Using Zustand
Include dependency in Project
npm install zustand
The updated Dependencies in package.json file will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4",
"zustand": "^4.5.2"
}
Example: This example implements the above-mentioned approach
// App.js import React, { useState } from 'react' ;
import useGlobalState from './GlobalStateStore' ;
import GlobalStateDisplay from './GlobalStateDisplay' ;
const App = () => { const [inputValue, setInputValue] = useState( '' );
const { count, setCount } = useGlobalState();
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
const updateGlobalState = () => {
const number = parseInt(inputValue);
if (!isNaN(number)) {
setCount(number);
}
};
return (
<div>
<input type= "number" value={inputValue}
onChange={handleInputChange} />
<button onClick={updateGlobalState}>
Update Global State</button>
<GlobalStateDisplay />
</div>
);
} export default App;
|
// GlobalStateDisplay.js import React from 'react' ;
import useGlobalState from './GlobalStateStore' ;
const GlobalStateDisplay = () => { const { count } = useGlobalState();
const squaredValue = count ** 2;
return (
<div>
<p>Global State:
{count}</p>
<p>Square of Global State:
{squaredValue}</p>
</div>
);
} export default GlobalStateDisplay;
|
// GlobalStateStore.js import create from 'zustand' ;
const useGlobalState = create((set) => ({ count: 0,
setCount: (value) => set({ count: value }),
})); export default useGlobalState;
|
Appraoch 5: Using XState
Include dependency in Project
npm install xstate @xstate/react
The updated Dependencies in package.json file will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@xstate/react": "^4.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4",
"xstate": "^5.9.1"
}
Example: This example implements the above-mentioned approach
// App.js import React, { useState } from 'react' ;
import useGlobalState from './GlobalStateStore' ;
import GlobalStateDisplay from './GlobalStateDisplay' ;
const App = () => { const [inputValue, setInputValue] = useState( '' );
const { state, send } = useGlobalState();
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
const updateGlobalState = () => {
const number = parseInt(inputValue);
if (!isNaN(number) && inputValue !== '' ) {
send({ type: 'SET_COUNT' , value: number });
setInputValue( '' );
}
};
return (
<div>
<input type= "number" value={inputValue}
onChange={handleInputChange} />
<button onClick={updateGlobalState}>
Update Global State</button>
<GlobalStateDisplay state={state} />
</div>
);
}; export default App;
|
// GlobalStateDisplay.js import React from 'react' ;
const GlobalStateDisplay = ({ state }) => { const count = state.context.count;
const squaredValue = count * count;
console.log(squaredValue);
return (
<div>
<p>Global State: {count}</p>
<p>Square of Global State: {squaredValue}</p>
</div>
);
}; export default GlobalStateDisplay;
|
// GlobalStateStore.js import { createMachine, assign } from 'xstate' ;
import { useMachine } from '@xstate/react' ;
const globalStateMachine = createMachine({ id: 'globalState' ,
initial: 'idle' ,
context: {
count: 0,
},
states: {
idle: {
on: {
SET_COUNT: {
actions: assign({
count: (_, event) => event.value,
}),
},
},
},
final: {
type: 'final' ,
},
},
}); const useGlobalState = () => { const [state, send] = useMachine(globalStateMachine);
return { state, send };
}; export default useGlobalState;
|
Output(Same for all Approaches):