6 Best practices for react state management in 2023.
Tags: react, redux, javascript, webdev
Published: 2022-12-29 13:20:34


There are several best practices to follow when managing state in a React application:

 
1.**Keep the state as minimal as possible**: It's important to only store data in the state that is necessary for the component to render. This can help to keep your application's state tree small and easy to manage.

```
import { useState } from 'react';

function MyComponent() {
  // Only store the data that is necessary for the component to render
  const [state, setState] = useState({
    userName: '',
    userId: ''
  });

  return (
    <div>
      {/* Render the state data */}
      <p>Username: {state.userName}</p>
      <p>User ID: {state.userId}</p>
    </div>
  )
}

```
2.**Avoid direct manipulation of the state:** It's a good practice to avoid directly manipulating the state object. Instead, use React's setState() method to update the state. This ensures that the component will re-render when the state changes, allowing the user interface to stay up-to-date.

```
import { useState } from 'react';

function MyComponent() {
  const [state, setState] = useState({
    userName: '',
    userId: ''
  });

  const handleNameChange = (event) => {
    // Use setState() to update the state, rather than directly manipulating the state object
    setState({
      ...state,
      userName: event.target.value
    });
  }

  return (
    <div>
      {/* Render the state data */}
      <p>Username: {state.userName}</p>
      <p>User ID: {state.userId}</p>

      {/* Use an event handler to update the state */}
      <input type="text" onChange={handleNameChange} />
    </div>
  )
}

```


3.**Use a state management library:** If your application becomes complex and has a lot of state to manage, it can be helpful to use a state management library like Redux or MobX. These libraries provide a predictable and centralized way to manage state in your application.

```
import { useReducer } from 'react';
import { createStore } from 'redux';

// Define the reducer function to handle state updates
function reducer(state, action) {
  switch (action.type) {
    case 'SET_USERNAME':
      return {
        ...state,
        userName: action.userName
      };
    case 'SET_USER_ID':
      return {
        ...state,
        userId: action.userId
      };
    default:
      return state;
  }
}

// Create the store with the reducer function
const store = createStore(reducer);

function MyComponent() {
  // Use the store's state as the component's state
  const [state, dispatch] = useReducer(reducer, store.getState());

  const handleNameChange = (event) => {
    // Dispatch an action to update the store's state
    dispatch({
      type: 'SET_USERNAME',
      userName: event.target.value
    });
  }

  return (
    <div>
      {/* Render the state data */}
      <p>Username: {state.userName}</p>
      <p>User ID: {state.userId}</p>

      {/* Use an event handler to update the state */}
      <input type="text" onChange={handleNameChange} />
    </div>
  )
}

```

4.**Use functional setState():** When updating the state based on the previous state, it's a good idea to use the functional form of setState(), which allows you to pass a function to setState() instead of an object. This function receives the previous state as an argument and returns the new state.

```
import { useState } from 'react';

function MyComponent() {
  const [state, setState] = useState({
    userName: '',
    userId: ''
  });

  const handleNameChange = (event) => {
    // Use the functional form of setState() to update the state based on the previous state
    setState((prevState) => ({
      ...prevState,
      userName: event.target.value
    }));
  }

  return (
    <div>
      {/* Render the state data */}
      <p>Username: {state.userName}</p>
      <p>User ID: {state.userId}</p>

      {/* Use an event handler to update the state */}
      <input type="text" onChange={handleNameChange} />
    </div>
  )
}

```

5.**Use the context API:** If you have state that needs to be shared across multiple components, consider using the React context API. This allows you to create a "context" that can be shared by any component in the tree, without having to pass props down manually through multiple levels of the component hierarchy.

```
import { useState } from 'react';

// Create the context with a default value
const UserContext = React.createContext({
  userName: '',
  userId: ''
});

function MyComponent() {
  // Use the context to get the state
  const { userName, userId } = useContext(UserContext);

  return (
    <div>
      {/* Render the state data */}
      <p>Username: {userName}</p>
      <p>User ID: {userId}</p>
    </div>
  )
}

function App() {
  // Use the useState hook to manage the state within the context provider
  const [state, setState] = useState({
    userName: '',
    userId: ''
  });

  const handleNameChange = (event) => {
    setState((prevState) => ({
      ...prevState,
      userName: event.target.value
    }));
  }

  return (
    // Wrap the component tree in the context provider to make the state available to all components
    <UserContext.Provider value={state}>
      <MyComponent />
      <input type="text" onChange={handleNameChange} />
    </UserContext.Provider>
  )
}

```

6.**Use the useReducer hook:** If you have complex state logic that involves multiple sub-values or the need to perform calculations based on the previous state, you may want to consider using the useReducer hook. This hook allows you to define a reducer function that handles state updates in a predictable way, similar to how Redux works.

```
import { useReducer } from 'react';

// Define the reducer function to handle state updates
function reducer(state, action) {
  switch (action.type) {
    case 'SET_USERNAME':
      return {
        ...state,
        userName: action.userName
      };
    case 'SET_USER_ID':
      return {
        ...state,
        userId: action.userId
      };
    default:
      return state;
  }
}

function MyComponent() {
  // Initialize the state with the reducer function
  const [state, dispatch] = useReducer(reducer, {
    userName: '',
    userId: ''
  });

  const handleNameChange = (event) => {
    // Dispatch an action to update the state
    dispatch({
      type: 'SET_USERNAME',
      userName: event.target.value
    });
  }

  return (
    <div>
      {/* Render the state data */}
      <p>Username: {state.userName}</p>
      <p>User ID: {state.userId}</p>

      {/* Use an event handler to update the state */}
      <input type="text" onChange={handleNameChange} />
    </div>
  )
}

```