State
State is data that changes over time. It can be managed at two levels
- Component Level: managed with
useStateoruseReducer - Global Level: shared across multiple components with Context API, Redux, Zustand or other state management libraries.
Context API
React’s built-in state management solution
- Create a single context called
UserContext
import React, { createContext, useState } from "react";
// Create Context
export const UserContext = createContext();
// Create Provider Component
export const UserProvider = ({ children }) => {
const [user, setUser] = useState({
personalDetails: {
name: "John Doe",
email: "john@example.com",
},
employmentDetails: {
company: "TechCorp",
role: "Frontend Developer",
},
});
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};- Provide the context so that other components can use it
import React from "react";
import ReactDOM from "react-dom";
import { UserProvider } from "./UserContext";
import PersonalInfo from "./PersonalInfo";
import EmploymentInfo from "./EmploymentInfo";
const App = () => (
<UserProvider>
<PersonalInfo />
<EmploymentInfo />
</UserProvider>
);
ReactDOM.render(<App />, document.getElementById("root"));- Create components that make use of the global context
//PersonalInfo Component
import React, { useContext } from "react";
import { UserContext } from "./UserContext";
const PersonalInfo = () => {
// accessing user data from UserContext
const { user, setUser } = useContext(UserContext);
return (
<div>
<h2>Personal Information</h2>
<p>Name: {user.personalDetails.name}</p>
<p>Email: {user.personalDetails.email}</p>
</div>
);
};
export default PersonalInfo;Pros:
- Built-in to React
- Easy to implement
- Ideal for Small to Medium-Sized Applications
- Great for Read-Heavy Applications
- great for applications where the global state is mostly accessed rather than frequently updated
Cons:
- Not Ideal for large-scale applications
- Triggers Unnecessary Re-renders:
- when a value inside the context updates,
- all components consuming that context re-renders
- Lack of Strict State Management
- All components can directly modify the context,
- lacks structure and predictability
- makes debugging difficult and increases the risk of unintended side effects
Redux
State management library, provides a centralised store to manage and share data across components efficiently
- Create a user slice
import { createSlice } from "@reduxjs/toolkit";
// Initial state
const initialState = {
personalDetails: {
name: "John Doe",
email: "john@example.com",
age: 28,
},
employmentDetails: {
company: "Tech Corp",
position: "Software Engineer",
},
};
// Create Redux slice
const userSlice = createSlice({
name: "user", // Name of the slice
initialState, // Default state for this slice
reducers: {
updateEmploymentDetails: (state, action) => {
// updates the employementDetails by appending a new key sent by actions.payload
state.employmentDetails = { ...state.employmentDetails, ...action.payload };
},
},
});
// Export actions and reducer
export const { updateEmploymentDetails } = userSlice.actions;
export default userSlice.reducer;- Create a Redux Store
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./userSlice";
const store = configureStore({
reducer: {
user: userReducer,
},
});
export default store;- Wrap the app component with the redux provider, and ensure that it has access to the store
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./store";
import App from "./App";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);import React from "react";
import PersonalInfo from "./PersonalInfo";
import EmploymentInfo from "./EmploymentInfo";
function App() {
return (
<div>
<h1>User Details</h1>
<PersonalInfo />
<EmploymentInfo />
</div>
);
}
export default App;- Consume Redux State in
PersonalInfo
import React from "react";
import { useSelector } from "react-redux";
const PersonalInfo = () => {
// selecting only personalDetails from the user state
const personalDetails = useSelector((state) => state.user.personalDetails);
return (
<div>
<h2>Personal Information</h2>
<p>Name: {personalDetails.name}</p>
<p>Email: {personalDetails.email}</p>
<p>Age: {personalDetails.age}</p>
</div>
);
};
export default PersonalInfo;- Consume and update state in
EmploymentInfo
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { updateEmploymentDetails } from "./userSlice";
const EmploymentInfo = () => {
// selecting only emplyomentDetails from the state
const employmentDetails = useSelector((state) => state.user.employmentDetails);
// dispatch, to dispatch the action
const dispatch = useDispatch();
//dispatch updateEmploymentDetails to update the employee details with new company name
const updateCompany = () => {
dispatch(updateEmploymentDetails({ company: "NewTech" }));
};
return (
<div>
<h2>Employment Information</h2>
<p>Company: {employmentDetails.company}</p>
<p>Position: {employmentDetails.position}</p>
<button onClick={updateCompany}>Update Company</button>
</div>
);
};
export default EmploymentInfo;Pros:
- Predictable State Management
- ensures consistent state updates through actions and reducers
- easier to debug
- Centralised State
- Efficient Re-renders
- Only components subscribe to updated state slices re-render, improving performance
- Time-travel debugging:
- Redux provides its devtools that allow tracking and reverting state changes
- Scalability & Maintainability
- Modular slices make scaling seamless
Cons:
- Boliderplate code
- need to write actions, reducers and store configuration
- Complexity for Small Apps
- It may be an overkill for simple applications
- Steep Learning Curve
- Increased Bundle Size