Opinionated React: State Management
Tags: react, redux, javascript, codequality
Published: 2020-03-06 15:40:12

# Intro
I’ve been working with React for over four years. During this time, I’ve formed some opinions on how I think applications should be. This is part 3 in the series of such opinionated pieces.

# What I'll be covering
There are a lot of parts to state management. I won't be able to cover them all in one sitting. For this post, I'll show you how I use plain React to manage state in my components. 

Make sure to follow me for my future posts related to state management, where I'll write about:

- Component level state vs global state
- Good use cases and my pattern for React context
- Status enums instead of booleans

# Just use React
Too often have I seen teams adopt state management libraries like Redux, MobX, or something else before using React‘s built in state management solution.

There's nothing wrong with these libraries, but they are not necessary to build a fully functioning React application. In my experience, it is significantly easier to use plain React.

If you have a reason to use one of these libraries instead of using `useState` or `useReducer`, please leave a comment because I would love to know your use case.

Next time you build a component, try using plain React.

# Hooks
I mentioned two hooks above, `useState` and `useReducer`. Here’s how I use each of them.

## Start with useState
I start by building my components with the useState hook. It’s quick and gets the job done.

```tsx
const MovieList: React.FC = () => {
  const [movies, setMovies] = React.useState<Movie[]>([])

  React.useEffect(() => {
    MovieService
      .fetchInitialMovies()
      .then(initialMovies => setMovies(initialMovies))
  }, [])

  return (
    <ul>
      {movies.map(movie => <li key={movie.id}>{movie.title}</li>}
    </ul>
  )
}
```

If we need another piece of state, simply add another `useState` hook

```tsx
const MovieList: React.FC = () => {
  const [isLoading, setIsLoading] = React.useState<boolean>(true)
  const [movies, setMovies] = React.useState<Movie[]>([])

  React.useEffect(() => {
    MovieService
      .fetchInitialMovies()
      .then(initialMovies => setMovies(initialMovies))
      .then(() => setIsLoading(false))
  }, [])

  if (isLoading) {
    return <div>Loading movies...</div>
  }

  return (
    <ul>
      {movies.map(movie => <li key={movie.id}>{movie.title}</li>}
    </ul>
  )
}

```
## useReducer when you have a lot of state

My limit for related pieces of state is 2. If I have 3 pieces of state that are related to each other, I opt for `useReducer`. 

Following the above example, let's say we wanted to display an error message if fetching the movies failed. 

_We could_ add another `useState` call, but I think it looks a bit messy 😢.

```tsx
export const MovieList: React.FC = () => {
  const [isLoading, setIsLoading] = React.useState<boolean>(true);
  const [movies, setMovies] = React.useState<Movie[]>([]);
  const [error, setError] = React.useState<string>("");

  const handleFetchMovies = () => {
    setIsLoading(true); // 😢
    setError(""); // 😢
    return MovieService.fetchInitialMovies()
      .then(initialMovies => {
        setMovies(initialMovies);
        setIsLoading(false); // 😢
      })
      .catch(err => {
        setError(err.message); // 😢
        setIsLoading(false); // 😢
      });
  };

  React.useEffect(() => {
    handleFetchMovies();
  }, []);

  if (isLoading) {
    return <div>Loading movies...</div>;
  }

  if (error !== "") {
    return (
      <div>
        <p className="text-red">{error}</p>
        <button onClick={handleFetchMovies}>Try again</button>
      </div>
    );
  }

  return (
    <ul>
      {movies.map(movie => (
        <li key={movie.id}>{movie.title}</li>
      ))}
    </ul>
  );
};

```

Let's refactor this to use `useReducer`, which will simplify our logic.

```tsx
interface MovieListState {
  isLoading: boolean;
  movies: Movie[];
  error: string;
}

type MoveListAction =
  | { type: "fetching" }
  | { type: "success"; payload: Movie[] }
  | { type: "error"; error: Error };

const initialMovieListState: MovieListState = {
  isLoading: true,
  movies: [],
  error: ""
};

const movieReducer = (state: MovieListState, action: MoveListAction) => {
  switch (action.type) {
    case "fetching": {
      return { ...state, isLoading: true, error: "" };
    }
    case "success": {
      return { ...state, isLoading: false, movies: action.payload };
    }
    case "error": {
      return { ...state, isLoading: false, error: action.error.message };
    }
    default: {
      return state;
    }
  }
};

export const MovieList: React.FC = () => {
  const [{ isLoading, error, movies }, dispatch] = React.useReducer(
    movieReducer,
    initialMovieListState
  );

  const handleFetchMovies = () => {
    dispatch({ type: "fetching" });
    return MovieService.fetchInitialMovies()
      .then(initialMovies => {
        dispatch({ type: "success", payload: initialMovies });
      })
      .catch(error => {
        dispatch({ type: "error", error });
      });
  };

  React.useEffect(() => {
    handleFetchMovies();
  }, []);

  if (isLoading) {
    return <div>Loading movies...</div>;
  }

  if (error !== "") {
    return (
      <div>
        <p className="text-red">{error}</p>
        <button onClick={handleFetchMovies}>Try again</button>
      </div>
    );
  }

  return (
    <ul>
      {movies.map(movie => (
        <li key={movie.id}>{movie.title}</li>
      ))}
    </ul>
  );
};
```

# Q&A
Every post I will answer a question I received on twitter. Here's this week's question.

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">My main problem is that I don&#39;t understand when I should use Redux. I don&#39;t hate Redux, I know how it works (on a simple way) but don&#39;t understand why hooks are not enough in comparison of state managers like Redux, Effector or MobX<br><br>would love to hear opinions about that</p>&mdash; ｖ１ｒｔｌ (@v1rtl) <a href="https://twitter.com/v1rtl/status/1233195515384356865?ref_src=twsrc%5Etfw">February 28, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

I don't use redux anymore. I haven't used it since React's context api was released. IMO, I think hooks + context are enough to build your application.

# Wrapping Up
This is the 3rd installment in a series of pieces I will be writing. If you enjoyed this, please comment below. What else would you like me to cover? As always, I’m open to feedback and recommendations.

Thanks for reading.

P.S. If you haven’t already, be sure to check out my previous posts in this series:

1. [An Opinionated Guide to React: Folder Structure and File Naming](https://dev.to/farazamiruddin/an-opinionated-guide-to-react-folder-structure-file-naming-1l7i)
2. [An Opinionated Guide to React: Component File Structure](https://dev.to/farazamiruddin/an-opinionated-guide-to-react-component-file-structure-1jg0)