Counter App in React with Redux

Author
By Darío Rivera
Posted On in React

In a previous article, we learned the Core concepts of Redux: The Store, Actions, and Reducers. Now it is time to build our first React application using Redux. We'll create a simple counter application that will show you how Redux works in a nutshell.

Creating a Redux Slice

The term "slice" refers to dividing the global root Redux state into smaller parts based on functional areas. In this instance, we will create a slice specifically for managing the counter state.

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    incremented: state => {
      state.value += 1
    },
    decremented: state => {
      state.value -= 1
    }
  }
})

You can also take a look at the property reducers. As we've learned in a previous article, a reducer defines how the store should change when a specific action happens. For the sake of this example, there are two possible actions called incremented and decremented which define how the state increments and decrements its value by one.

This code is usually shipped in a separate file, let's say counter.js, so you might want to add the following code at the end to export the available actions and the reducer object.

export const { incremented, decremented } = counterSlice.actions;
export default counterSlice.reducer;

Configuring The Store

Next, we create the centralized Redux store. A store holds the whole state tree of your application.

// import reducer if resides in a separate file
import counterReducer from "./src/counter";

const store = configureStore({
  reducer: {
    // hook up any slices here
    counter: counterSlice.reducer
  }
})

Here, counter becomes a state section inside the store and counterSlice.reducer manages that section.

Creating the React Component

Now we build the UI. Take a look at how the dispatch method triggers the specific action letting Redux to handle the changes in the state.

import { useDispatch } from 'react-redux';
import { incremented, decremented } from '../src/counter';

function App() {
  const counter = useSelector(state => state.counter.value)
  const dispatch = useDispatch()
  const actions = counterSlice.actions

  return (
    <>
    <p>Counter: {counter}</p>
      <div>
        <button onClick={() => dispatch(actions.incremented())}>+</button>
        <button onClick={() => dispatch(actions.decremented())}>-</button>        
      </div>
    </>
  )
}

The following is a working example in CodePen.



Passing Data Through Actions

The reducer functions always receive the state as their first parameter, allowing for easier modifications. An optional action can be passed as a second parameter so that we can send useful information to help reducers modify the state.

The following modification enables you to specify the value by which the counter will be incremented.

incremented: (state, action) => {
  state.value += action.payload
}

This way a button can increase the state by defining the value as the function parameter.

<button onClick={() => dispatch(actions.incremented(1))}>+1</button>
<button onClick={() => dispatch(actions.incremented(2))}>+2</button>

Here's how this looks like on CodePen.


Acerca de Darío Rivera

Author

Application Architect at Elentra Corp . Quality developer and passionate learner with 10+ years of experience in web technologies. Creator of EasyHttp , an standard way to consume HTTP Clients.

LinkedIn Twitter Instagram

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.