Redux for State Management
Tags: react, redux, webdev, javascript
Published: 2023-07-30 06:31:57

Redux is a predictable state container for JavaScript applications that can help you manage your application’s global state in a more organized and maintainable way. The key concepts of Redux are the store, reducers, actions, and dispatch.

Store: The store is the single source of truth for your application’s state. It’s a plain JavaScript object that holds all of the application’s state data. The store is created using the `createStore` function from the Redux library, and it's typically created at the top-level of the application.

Reducers: Reducers are pure functions that take the current state and an action, and return a new state based on the action. Reducers are responsible for updating the store’s state in response to actions dispatched by the application. The state returned from the reducer must be a new object, and not a mutated version of the previous state. Here’s an example of a simple reducer:

```
function counterReducer(state = { count: 0 }, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}
```
In the example above, the reducer updates the state based on two actions: `INCREMENT` and `DECREMENT`. When an `INCREMENT` action is dispatched, the reducer returns a new object with the count property incremented by 1. Similarly, when a `DECREMENT` action is dispatched, the reducer returns a new object with the count property decremented by 1.

Actions: Actions are plain JavaScript objects that describe changes to the application’s state. They must have a `type` property that describes the type of action being performed, and can also include any additional data needed to perform the action. Actions are typically created using action creator functions, which are functions that return the action object. Here's an example of an action creator function:

```
function increment() {
  return { type: 'INCREMENT' };
}
```
In the example above, the `increment` function returns an action object with a type of `INCREMENT`.

Dispatch: Dispatch is the function used to send actions to the store. When an action is dispatched, the store calls the reducers with the current state and the action, and the reducers return the new state. The store then updates its state with the new state returned by the reducers. Dispatch is typically called from within components or other parts of the application that need to update the state. Here’s an example of how to use dispatch to update the count:

```
import { createStore } from 'redux';
import counterReducer from './counterReducer';

const store = createStore(counterReducer);
store.dispatch({ type: 'INCREMENT' });
```
In the example above, the `createStore` function creates a new store using the `counterReducer`. Then, the `dispatch` function is used to send an `INCREMENT` action to the store, which updates the count in the store's state.

One of the benefits of using Redux is that it can simplify your application’s architecture and make it more testable and maintainable. By keeping all of your state in a single, centralized location, you can easily reason about changes to your data over time. Additionally, because Redux relies on pure functions and plain JavaScript objects, it’s easy to test and debug your code.

Testability is another sweet key feature of using Redux is that it makes your application more testable. Since your state management is centralized and predictable, it becomes easier to write tests that ensure your application is behaving as expected. By writing tests for your actions and reducers, you can be confident that your state changes are working correctly and that you’re not introducing regressions as you add new features.

```
describe('incrementCounter', () => {
  it('should increment the counter by 1', () => {
    const initialState = { count: 0 };
    const action = { type: 'INCREMENT_COUNTER' };
    const newState = reducer(initialState, action);
    expect(newState.count).toEqual(1);
  });
```
The code above defines a test for the `incrementCounter` action using the Jest testing framework. The test sets up an initial state, dispatches the action, and expects the new state to have a count of 1. This test demonstrates how Redux makes it easy to test individual actions and their effects on the state of the application.

While Redux is a powerful tool, it may not be the best choice for every project. For example, if you have a small application with simple state management needs, you might be better off using [React Context](https://legacy.reactjs.org/docs/context.html) or another simpler solution like [Zustand](https://docs.pmnd.rs/zustand/getting-started/introduction) which I’m going to using in one of my upcoming projects. It all comes down to your project’s use cases and specificities.

Finally I highly recommend reading the [Redux](https://redux.js.org/tutorials/essentials/part-1-overview-concepts) documentation to get started and gradually go in depth in each concept. It definitely helped me have a better understanding quicker than watching Youtube tutorials about it and also to write this article. I decided to write this to keep it as a reference for me so hopefully you find it useful as well. Have a good read!