DEV Community

Matt Eddy
Matt Eddy

Posted on • Updated on

React - Redux In Less Then 7 Minutes

Alt Text

Attention!!!

The code shown in this article is a valid implementation of the redux pattern referenced from the Official Redux.js.org. However, redux has provided us with an enhanced toolkit which they recommend using when developing with react and redux. It is completely your decesion on which path you take. You can learn more about reduxjs/toolkit by checking out the link.

Overview

In this article, I will cover some fundamental concepts of redux and how to build a simple application(Counter App) using react and redux.

Redux

Redux is predictable state container for javascript applications. This means whenever there are changes within the application including both data and UI changes, those changes are contained in a single javascript object called the state. The state is often referred to as the single-source-of-truth because it is never mutated or modified, but instead recreated. With a single-source-of-truth we can better predict the state of the application at a giving moment.

Redux Fundamentals

Before diving into the application lets cover some Redux fundamentals.

State

The state is a javascript object that represents the entire state of a redux application. It can be a simple object with a single value or a more complex object.

{counter: 0}
Enter fullscreen mode Exit fullscreen mode

The state is accessible throughout the entire application and is managed by a centralized container known as the store. The only way to access the store is by dispatching an action.

Actions

An action is an description of how the store should change the state.

{
  type: 'INCREMENT',
  value: 5, 
}
Enter fullscreen mode Exit fullscreen mode

The change within the application is understood by the action's type property. All actions have a type property. The type property explains to the store how to respond and recreate the state. The creation of the state is handled by a reducer.

Reducers

A reducer is a javascript function that will create a new state based on some action type.

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case actionType.ADD:
            return {
                ...state,
                counter: state.counter + 1
          }
    }
    return state;
}
Enter fullscreen mode Exit fullscreen mode

Reducers are often referred to as pure javascript functions. A pure javascript function is a function that giving the same input will always return the same output. They are called pure functions because they are predictable and include no side effects such as network or database calls. The most import job of a reducer is to recreate the state for the store.

Store

A store is a javascript object that holds the application's state. There should only be a single store in a redux application.

{
  dispatch: Dispatch
  getState: () => State
  subscribe: (listener: () => void) => () => void
  replaceReducer: (reducer: Reducer) => void
}
Enter fullscreen mode Exit fullscreen mode

Store Methods

  • dispatch(action): Dispatches an action
  • getState(): Returns the current state
  • subscribe(listener): Adds a change listener
  • replaceReducer(nextReducer): Replaces the reducer

Redux Pattern

The redux pattern can visualized as below.
Alt Text

React and Redux (Counter App)

In this section, I will cover the steps to build a simple counter application with react and redux. To see the application Code.

Getting Started

First, I'll create a basic react app with npx and install the needed dependencies: react-redux and redux.

npx create-react-app redux-practice
cd redux-practice
npm i react-redux redux
Enter fullscreen mode Exit fullscreen mode

Next, I'll create three new directories to manage the files of my application.

mkdir src/store
mkdir src/containers
mkdir src/components
Enter fullscreen mode Exit fullscreen mode
  • src/store - Reducers and actions needed for redux store
  • src/containers - Components connected to the redux store
  • src/components - Presentational level components

I'll start off by working on the actions and reducers which will be located in the the store directory.

touch src/store/actions.js
touch src/store/reducers.js
Enter fullscreen mode Exit fullscreen mode

First, I'll define my actions. This application wont have very many for the sake of simplicity and demonstration.

src/store/actions.js
export const ADD = 'ADD';
export const SUBTRACT = 'SUBTRACT';
Enter fullscreen mode Exit fullscreen mode

Next, I'll create the reducer function needed for the store.

src/store/reducers.js
import * as actionType from './actions';
const initialState = {
    counter: 0
}

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case actionType.ADD:
            return {
                ...state,
                counter: state.counter + 1
            }
        case actionType.SUBTRACT:
            return {
                ...state,
                counter: state.counter - 1
            }
        default:
            return state
    }
}
export default reducer;
Enter fullscreen mode Exit fullscreen mode

In the above code snippet I've imported all the actions from actions.js , then created a state variable to initialize the state for this reducer, and created my reducer function. The reducer function takes two arguments state, which if not initialized will be set to the initialState, and action which will be passed in once an action is dispatched. I'll use a switch state to determine the action type and using the actions from actions.js handle each case accordingly.

Now that I have my reducer and actions created I'll create my store. The store should be created at the top level component which in this case is index.js

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './store/reducers';
import './index.css';
import App from './App';

const store = createStore(reducer);

ReactDOM.render(
  <React.Fragment>
    <Provider store={store}><App /></Provider>
  </React.Fragment>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

In the index.js file I imported createStore from redux, as well as Provider from react-redux. I also imported my reducers which I will need for my createStore function. The createStore function takes in the reducers and returns the redux store. I use the Provider which has a prop called store and I pass the store created above to the Provider prop. At this point the redux store is accessible throughout the entire react application.

Next, I'll create two components to represent the counter application.

mkdir src/components/CounterButton
mkdir src/components/CounterLabel
touch src/components/CounterButton/CounterButton.js
touch src/components/CounterLabel/CounterLabel.js
Enter fullscreen mode Exit fullscreen mode

These components are presentational components so they will be very simple.

CounterButton.js
import React from 'react';
import './CounterButton.css';

function CounterButton(props) {
    return (
        <div className="CounterButton">
            <button
                type="button"
                onClick={props.clicked}
                className="btn btn-primary">
                {props.label}
            </button>
        </div>
    )
}
export default CounterButton;
Enter fullscreen mode Exit fullscreen mode
CounterLabel.js
import React from 'react';

import './CounterLabel.css'

function CounterLabel(props) {
    return (
        <div className='CounterLabel'>
            {props.value}
        </div>
    )
}

export default CounterLabel;
Enter fullscreen mode Exit fullscreen mode

Next, I'll create the counter component which will be connected to the redux store.

touch src/containers/Counter.js
Enter fullscreen mode Exit fullscreen mode
Counter.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actionType from '../store/actions';
import CounterLabel from
    '../components/CounterLabel/CounterLabel';
import CounterButton from
    '../components/CounterButton/CounterButton';

import './Counter.css';

class Counter extends Component {
    render() {
        return (
            <div className="Counter">
                <CounterLabel value={this.props.ctr} />
                <CounterButton
                    clicked={this.props.onAdd}
                    label="Add" />
                <CounterButton
                    clicked={this.props.onSubtract}
                    label="Subtract" />
            </div>
        )
    }
}

const mapStateToProps = state => {
    return {
        ctr: state.counter
    }
}

const mapDispatchToProps = dispatch => {
    return {
        onAdd: () => dispatch({ type: actionType.ADD }),
        onSubtract: () => dispatch({type: actionType.SUBTRACT})
    }
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Counter);

Enter fullscreen mode Exit fullscreen mode

Within the Counter.js I import the connect function from react-redux package which is used directly on the component at the end of the file. The connect function takes two arguments, first mapStateToProps which is responsible for selecting a part of the state that the component needs, and second mapDispatchToProps which is responsible for dispatching actions to the store. The onAdd and onSubtract functions are passed as props to the ControlButtons and when clicked dispatch their respective actions to the store. At this point are simple counter application is complete.

When To Use Redux

The application in this article is very simple for learning purposes. In most cases you will not need redux for an application of this size. A state management system is good for large scale applications where state management is difficult to understand. Here are a few pointers on when to use redux that I got from Maxillian over at Academind.

  1. Local UI State - Redux not recommended
  2. Persistent State - Redux can be used for the data you need to display
  3. Client State - Look to use Redux

Alt Text

Conclusion

As always take care and if you found this article helpful please leave a rating or if you have a question leave a comment and I'll try to get back to you as soon as possible.

Oldest comments (11)

Collapse
 
joelpatrizio profile image
Joel Patrizio

Awesome, thanks for sharing!

Collapse
 
markerikson profile image
Mark Erikson

Hi, I'm a Redux maintainer. Please note that "modern Redux" code is very different than what most older tutorials show. We've introduced newer APIs like Redux Toolkit, which is a set of utilities that provide a light abstraction to simplify the most common Redux tasks, and the React-Redux hooks API, which is generally easier to use than the traditional connect API.

I strongly recommend reading through the newly rewritten official tutorials in the Redux docs, which have been specifically designed to teach you how Redux works and show our recommended practices:

  • "Redux Essentials" tutorial: teaches "how to use Redux, the right way", by building a real-world app using Redux Toolkit
  • "Redux Fundamentals" tutorial: teaches "how Redux works, from the bottom up", by showing how to write Redux code by hand and why standard usage patterns exist, and how Redux Toolkit simplifies those patterns

The older patterns shown in almost all other tutorials on the internet are still valid, but not how we recommend writing Redux code today.

You should also read through the Redux "Style Guide" docs page, which explains our recommended patterns and best practices. Following those will result in better and more maintainable Redux apps.

In addition, the easiest way to start a new project is with the official Redux+JS template for Create-React-App. It comes with Redux Toolkit and the React-Redux hooks API already set up when the project is created.

Collapse
 
meddy672 profile image
Matt Eddy • Edited

Thank you for the feedback. How old are the methods used in this tutorial compared to that of the redux toolkit?

Collapse
 
markerikson profile image
Mark Erikson

The patterns shown here are the original Redux usage patterns that we've shown since day 1: hand-written immutable update logic, SCREAMING_SNAKE_CASE constants, separate "actions" and "reducers" files, and use of connect.

The pieces that we now call "modern Redux" have come out over time:

  • The React-Redux hooks API was launched in React-Redux v7.1, in June 2019
  • The Redux "Style Guide" page was published in fall 2019
  • Redux Toolkit 1.0 was released in October 2019
  • Last year I rewrote the Redux docs tutorials from scratch. I published the "Essentials" tutorial around June, and the "Fundamentals" tutorial at the end of October
Collapse
 
meddy672 profile image
Matt Eddy

Also how do you become an active contributor of the redux api? I think this would be valuable information for those who are looking for ways to get involved with an open source project. May we connect with directly?

Collapse
 
markerikson profile image
Mark Erikson

I'm always interested in having folks contribute to Redux! Now, having said that: the Redux core library is stable and there's no active development work going on there. Same with React-Redux for now.

There is some active development work going on with Redux Toolkit, but it's primarily myself and a couple other maintainers atm.

Beyond that, there's a ton of work that I want to do with the Redux docs that I don't have time to do all by myself:

github.com/reduxjs/redux/issues/3592

and I'd definitely love to have folks get involved with that effort. (I actually got started with Redux by contributing the Redux FAQ to the docs back in early 2016.)

I hang out in the #redux channel in the Reactiflux Discord ( reactiflux.com ). Feel free to drop by and say hi.

Thread Thread
 
meddy672 profile image
Matt Eddy

I will check this out myself and also advertise this on this article.

Collapse
 
meddy672 profile image
Matt Eddy

I was unable to access the discord channel, but I would be happy to knock out a few of those issues you have on your repository. Do we just fork the repository and work the issues as described?

Collapse
 
markerikson profile image
Mark Erikson

Hmmm. Anyone should be able to sign up for a Discord account if you don't have one yet, and then join the Reactiflux discord. We do have a welcome flow where you have to agree to the community guidelines before you can access any other channels, so make sure you tried that.

If there's a particular issue you're interested in working on, leave a comment in that issue thread and we can discuss details.

Thread Thread
 
meddy672 profile image
Matt Eddy • Edited

I was able to get access to the channel taking a moment to review the welcome content and I have left a comment on the repo. #3591

Collapse
 
adambiggs profile image
adam-biggs

Thanks for the post Matt!

One of the best and most overlooked alternatives to Redux is to use React's own built-in Context API.

Context API provides a different approach to tackling the data flow problem between React’s deeply nested components. Context has been around with React for quite a while, but it has changed significantly since its inception. Up to version 16.3, it was a way to handle the state data outside the React component tree. It was an experimental feature not recommended for most use cases.

Initially, the problem with legacy context was that updates to values that were passed down with context could be “blocked” if a component skipped rendering through the shouldComponentUpdate lifecycle method. Since many components relied on shouldComponentUpdate for performance optimizations, the legacy context was useless for passing down plain data.

The new version of Context API is a dependency injection mechanism that allows passing data through the component tree without having to pass props down manually at every level.

The most important thing here is that, unlike Redux, Context API is not a state management system. Instead, it’s a dependency injection mechanism where you manage a state in a React component. We get a state management system when using it with useContext and useReducer hooks.

A great next step to learning more is to read this article by Andy Fernandez: scalablepath.com/react/context-api...