In this post, we’ll walk through an example of a simple shopping basket web application to understand the core concepts and building blocks behind React and Redux.
First of all, you can find and clone the project code under Koders Github page. As you can see below, the application is quite simple but it will hopefully help us to understand some basic concepts behind React and Redux and it could also be helpful in terms of applying Styled Components and using React Testing Library as well.
The project uses
create-react-app to create the application template. If you wish to create your own project, you could do so by issuing this command (if you have
npx create-react-app <app-name>
There are a few things that we expect our shopping basket to do:
- It should calculate and display the line totals and the grand total on the bottom of the page in real-time, based on the item amount changes.
- Items can be removed and totals should be updated accordingly.
- When all items are removed, a warning that states there are no items in the basket should be displayed.
Clearbutton should set all the amounts to zero but all items should stay in the basket.
Checkoutbutton won’t do anything, we could just display a message.
This is a very simple application that actually doesn’t require the usage of Redux or any other state management library but it’s being used only for demonstration purposes and it can also be thought as a part of a bigger application or a shopping website.
So I’d like to start with building our Redux store, which will drive our application structure.
As per our user stories above, the application could have the following actions:
- Updating an item
- Removing an item
- Clearing all items
We will start with a static list of items and won’t deal with the scenario of adding new items into the basket. So we can start with implementing these actions and updating our Redux store accordingly.
I’d highly recommend you to apply TDD principles while building any application and start with the test first to keep the solution as simple and robust as possible.
Let’s have a look at
store.test.js file in the project to see the test cases we’ve written for the store:
Of course, while applying TDD, we wouldn’t write all the tests at once and write one test and write the absolute minimum code to pass the test, but here we have all of them.
For those who are not that familiar with Redux, we could simply say that it’s a library to help simplify state management in JS applications.
In Redux, there are some fundamentals that we need to be familiar with.
- First, we have the store, which can be thought of as an in-memory database in the form of a single JSON object that holds the application state.
- Then we have actions (which we have described above), which define the actions that can be applied to the store.
- Then we have reducers, which are basically stateless functions that operate on the state or a part of it. They work based on the defined actions and create a new version of the store when they need to change it.
Redux embraces and utilises functional concepts and thus we never directly modify the store and always create a new version of it in the reducer logic.
As you can see in the basket reducer code, we first define the actions as constant and we always return a new version of the store in the reducer code when we want to make any changes in the store. Redux manages the rest.
So now we have our store in place with every action we need, we can focus on the UI & React side.
Here we see the tests built with React Testing Library and Jest. I really like the approach of RTL in terms of UI testing, because it actually tests the generated HTML code by React so we don’t need to focus on component-level details and focus on the actual output of the components and the application.
In this test, we test the user scenarios we’ve defined before. Whether or not the items are being rendered properly, updates are propagated and so on. More tests should be added to test removal for instance, but I’m leaving those test to you 🙂
As you can see, by calling
render from RTL and using utility functions provided by it to select and interact with the items on the UI, we’re testing the actual UI output to be seen by the user. So RTL tests can be considered as UI tests on the component level. Depending on the granularity of the test, they can be used to implement UI tests as well. And here you can see that we are actually testing the whole application functionality in a single test rather than testing individual component behaviour.
So here is the basket screen implementation. Here we have a functional React component that utilises Styled Components and connected to Redux store.
Styled components enable us to build UI components using plain CSS and JS inside template strings. I think it’s a really nice way to build UI components as it fits more to React way of building a web applications by changing the concept of Separation of Concerns. As you may already know, React focuses on the functionality of a component and addresses the separation of concerns as the separation between the functionality of the parts of the UI, rather than of different technologies like JS, CSS and HTML. I think it makes great sense and I find Styled Components fits in this way of thinking better than technologies like SASS, Less or plain CSS.
On the Redux integration side of the component, we are using
connect function from Redux to connect the component to Redux store. In this way, whenever a relevant state change happens, the change will be received as a property update (basket) by the component, thanks to
mapDispatchToProps methods provided by Redux.
As its’ name states,
mapStateToProps maps the required part of the state (state.basket in this case) as a component property (basket). On the other hand,
mapDispatchToProps maps actions defined in the store to properties with defined names, so that whenever an action is triggered that requires a state change in Redux store, we can call mapped property with proper payload to trigger the execution of reducer for that action and update the state accordingly. After the state update, Redux will send the new values of the state to the component and it will trigger a new render of the component if needed.
I’ll leave the rest of the code for you to discover. In this simple example, we have tried to demonstrate and understand the basics of a Redux powered React application. If you’d like to learn more about React & Redux and how to build real-world web applications, please feel free to get in touch and join our courses.
Good luck and all the best!