Managing React State Like A Superhero 🦸
Tags: react, javascript, typescript, webdev
Published: 2022-12-25 13:17:22

The state is an important concept in React, as it allows components to store and manage data that can change over time. Properly managing state can help make your React application more efficient and easier to maintain. In this article, we will explore different ways to manage state in React and provide code examples for each method.


**1- Using the useState Hook:**

One of the most common ways to manage state in React is by using the `useState` hook. This hook allows you to add state to functional components, which were previously unable to have state. Here is an example of how to use the `useState` hook:


```ts
import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
```
In this example, we are using the `useState` hook to create a piece of state called `count`, which is initially set to 0. The `useState` hook returns an array with two elements: the current value of the state (in this case, `count`) and a function to update the state (in this case, `setCount`). We can use the `setCount` function to update the value of `count` by passing a new value as an argument.


**2- Using the useReducer Hook:**

Another way to manage state in React is by using the `useReducer` hook. This hook is similar to the `useState` hook, but it allows you to manage complex state logic with reducers, which are functions that take in the current state and an action, and return a new state. Here is an example of how to use the `useReducer` hook:

```ts
import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Example() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>You clicked {state.count} times</p>
      <button onClick={() => dispatch({ type: 'increment' })}>
        Increment
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        Decrement
      </button>
    </div>
  );
}
```

**3- Using the useContext Hook:**
Another way to manage the state in React is by using the `useContext` hook in combination with the `createContext` function. The `createContext` the function allows you to create a context object, which can be used to pass data down the component tree without the need for props drilling. The `useContext` hook allows you to access the context object from a functional component. Here is an example of how to use them `createContext` && `useContext` hooks:

```ts
import React, { createContext, useContext } from 'react';

const CountContext = createContext();

function Example() {
  const count = useContext(CountContext);

  return (
    <div>
      <p>You clicked {count} times</p>
    </div>
  );
}

function App() {
  const [count, setCount] = useState(0);

  return (
    <CountContext.Provider value={count}>
      <Example />
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </CountContext.Provider>
  );
}
```

**4- Using a Higher-Order Component:**

Another way to manage the state in React is by using a higher-order component (HOC). A HOC is a function that takes a component as an argument and returns a new component with additional functionality. Here is an example of how to use a HOC to manage the state:


```ts
import React from 'react';

function withState(WrappedComponent) {
  return class extends React.Component {
    state = { count: 0 };

    increment = () => {
      this.setState({ count: this.state.count + 1 });
    }

    render() {
      return (
        <WrappedComponent
          count={this.state.count}
          increment={this.increment}
          {...this.props}
        />
      );
    }
  };
}

function Example(props) {
  return (
    <div>
      <p>You clicked {props.count} times</p>
      <button onClick={props.increment}>
        Click me
      </button>
    </div>
  );
}

const EnhancedExample = withState(Example);

function App() {
  return (
    <EnhancedExample />
  );
}

```

In this example, we are using a HOC called withState to add state management functionality to the `Example` component. The `withState` HOC returns a new component that has a `count` state and an `increment` function, which updates the `count` state. The `Example` component receives the `count` and `increment` props from the HOC and uses them to render the component.


**5- Using a State Management Library:**
Finally, another way to manage the state in React is by using a state management library.

Using a state management library is a popular approach to managing the state in larger React applications. A state management library is a separate package that provides additional functionality for managing the state, such as support for handling asynchronous actions and managing a global state.

One popular state management library for React is Redux. Redux is a predictable state container for JavaScript applications that helps you write applications that behave consistently. It works by creating a store that holds the application's state and provides functions for updating the state in a predictable way.

Here is an example of how to use Redux to manage the state in a React application:


```ts
import React from 'react';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';

const initialState = { count: 0 };

function reducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

const store = createStore(reducer);

function Example(props) {
  return (
    <div>
      <p>You clicked {props.count} times</p>
      <button onClick={props.increment}>
        Increment
      </button>
      <button onClick={props.decrement}>
        Decrement
      </button>
    </div>
  );
}

function mapStateToProps(state) {
  return {
    count: state.count
  };
}

function mapDispatchToProps(dispatch) {
  return {
    increment: () => dispatch({ type: 'INCREMENT' }),
    decrement: () => dispatch({ type: 'DECREMENT' })
  };
}

const EnhancedExample = connect(
  mapStateToProps,
  mapDispatchToProps
)(Example);

function App() {
  return (
    <Provider store={store}>
      <EnhancedExample />
    </Provider>
  );
}
```

In this example, we are using Redux to create a store that holds the application's state and a reducer function that updates the state in a predictable way. We are then using the `connect` function from the `react-redux` library to connect the `Example` component to the Redux store. The `connect` function returns a new component that receives the state and dispatch functions as props. The `Example` component uses the count prop to render the component and the `increment` and `decrement` props to update the state.

------------------------------------------------

In conclusion, there are several different ways to manage the state in React, each with its own advantages and disadvantages. It is important to choose the right approach for your specific use case and to consider factors such as the complexity of your state, the size of your application, and the needs of your team.