2018/03/07

Front-end

Replace Redux with React Context

Not that long ago, React 16.3 was released with a lot of new features and one of them was a new React Context. Before 16.3 you could use the Context but it was experimental. Some libraries, like React Router and React Redux, were using the old Context. The old and new Context are really different. If you’re going to use the new Context in your app, then you need to refactor your logic behind it, the API is not backwards compatible.

First of all, this article is not saying that using Redux is a bad thing (not at all), you can see it more as a hint that have to look at your application and ask yourself whether using Redux is not overkill. From now on you can use React Context to replace some of the functionality that normally would be added to Redux, for example hide and show a sidebar and storing that state.

Small context about the context

React Context is a way for a child component to access a value in a parent component. React Context has been invented because React had an issue with prop drilling, putting it simply, you need to access the state from the top of your React tree to the bottom and you end up passing props through components that do not necessarily need them. So it’s really hard to maintain.

React Context solves the problem of props drilling. It allows you to share props or state with an indirect child or parent.

Why would you replace Redux by the context?

Redux can be a difficult thing to get started with. When you use it for the first time, you have to learn some of the logic behind how it works, for example, learn more about store, reducers, actions, integrating inside the React application. For some people it’s a lot to keep everything connected and you have to write some extra code to make it work. In simple terms, sometimes implementing Redux inside your application is overkill. Redux is a really good system to scale your application.

Examples

Before I show some examples of how to use the React Context, I’m going to show how to make a counter component without Redux. Then we’ll look at how to do it with the new React Context and with Redux.

import React, { Component } from "react";

class Counter extends Component {
  state = { value: 0 };
  
  increment = () => {
      this.setState(prevState => ({ value: prevState.value + 1 }));
  };
  
  decrement = () => {
      this.setState(prevState => ({ value: prevState.value - 1 }));
  };
  
  render() {
    return (
      <div>
        {this.state.value}
        <button onclick={this.increment}>+</button>
        <button onclick={this.decrement}>-</button>
      </div>
    );
  }
}

Example one: This is a simple example of how you can make a counter that saves the state inside the React state but you can choose not to use the state if you want to.

import React, { Component } from "react";
import { createStore } from "redux";
import { connect } from "react-redux";

const reducer = (state = { value: 0 }, action) => {
  switch (action.type) {
    case "INCREMENT":
      return { value: state.value + 1 };
    case "DECREMENT":
      return { value: state.value - 1 };
    default:
      return state;
  }
};

const store = createStore(reducer);

const mapStateToProps = (mapState, ownProps) => {
  return { value: mapState.value };
};

class Counter extends Component {
  increment = () => {
    this.props.dispatch({ type: "INCREMENT" });
  };

  decrement = () => {
    this.props.dispatch({ type: "DECREMENT" });
  };

  render() {
    return (
      <Provider store={store}>
        {this.props.value}
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
      </Provider>
    );
  }
}

export default connect(mapStateToProps)(Counter);

Example two: This is the same as plain React but with Redux integration. Here you need a store, actions and reducers to make everything work. The value of the counter is reusable in another component, so long as you’re inside the provider component.

import React, { createContext } from "react";
const Context = createContext();
const { Provider, Consumer } = Context;

class App extends Component {
    state = { value: 0 };
    
    increment = () => {
        this.setState(prevState => ({ value: prevState.value + 1 }));
    };
    
    decrement = () => {
        this.setState(prevState => ({ value: prevState.value - 1 }));
    };
    
    render() {
      return (
        <Provider
          value={{
            value: this.state.value,
            increment: this.increment,
            decrement: this.decrement
          }}
        >
          <Counter />
        </Provider>
      );
    }
}

class Counter extends Component {
  render() {
    return (
      <Consumer>
        {({ value, increment, decrement }) => {
          <div>
            {value}
            <button onClick={increment}>+</button>
            <button onClick={decrement}>-</button>
          </div>;
        }}
      </Consumer>
    );
  }
}

Final example: As the last example, with the new Context. When making a Context createContext) you have two types: provider and consumer. We use the app as some kind of store and that’s where the provider is used. Setting the value will use render props, those render props can be used inside the application, so long you use consumer (from the created context).


Inside <Counter/> you have the consumer with the render props: value, increment, decrement. Use those to access everything from app.js (a kind of store but it isn’t).

When would you use Redux?

  • You want to store one global state(easy to debug, history, no overwrites)
  • You have a really big app
  • You want to scale your app to a multiple instances
  • You have a predictable state container

When would you use React Context without Redux?

  • Small app, for example: a small website with just text and pictures
  • Less complicated to set up
  • Weight(less coding), performance, easier to implement
  • Cleaner action return with state chunk (use of setState)

Conclusion:

You can use React Context when all you want is a simple state management, or in cases where you want to pass some props deeply without the need for implementing Redux or Mobx. Take a look at your code and ask yourself whether you really need to use Redux at this point and also whether it would ever grow so much that you really need state management. For small applications, where you need some small state management, you can use the new Context, which is easy to implement and could have the same functionality as Redux. When you need to scale, Redux is much better to use. Less is more!

Interesting articles that you must read: