One of the last requirements I had to fulfill in obtaining my degree in Computer Science was to work on an honours project. After much deliberation, I decided that I wanted to build a mobile app using React Native. I came to this conclusion because I wanted to challenge myself to learn something new and receive feedback on it. I knew absolutely nothing about React Native except that it was based off of React (which I also knew nothing about) and it was cross platform, meaning that I wouldn’t have to build separate versions for iOS and Android.
First step: I took a crash course in React Native. I dedicated a couple hours to learning about components, states, props, and all the basic blocks. I took this approach because I learn best by actually doing the work, not just studying the concepts. Long story short, I built this mobile app within weeks, then I had to write a paper about it. Writing this paper was mostly a breeze but one section that made me pause and think was the ‘further developments’ section. I thought about this critically and decided that if I were to continue working on this app, one major improvement would be to use Redux. Here begins my journey with Redux….well not yet.
Fast forward to a year and a half later, I picked up what I thought would be a minor front end tweak from the github board at work. Turns out this wasn’t so minor for me because it threw me right into the centre of Redux, and I’m talking advanced Redux – with middleware and async actions. I had to take a step back and find a crash course in Redux (are we sensing a pattern here?), and here’s what I learned.
Redux acts as a central data store for all the components within the app – this way, components do not need to be linked in the inheritance hierarchy to share data, and part of the problem of deciding which components to house some states in. Because I built my RN app without using Redux, I found myself in this very dilemma of deciding what components certain parts of the data would live in. This affected some of my design decisions and resulted in me changing up the shape of some pre-planned components.
The store houses the app’s state, which is where the data lives. This state can be accessed using getState() and updated using dispatch(action). An action is basically a unit of change, in the form of data, that specifies some modification to be made on the state. Actions are created by action creators (very clever naming, I know 😐).
Lesson #1: Actions should be as simple as possible, pass only the necessary data
For example, an action for when a button is clicked can be defined as:
The corresponding action creator would be:
This action can then be dispatched like so:
Next come the reducers – these are like state setters. Reducers define how the actions are handled and how the state is updated. It is usually best practice to have a default state to fall back on in case bad data is handled by the reducer.
Lesson # 2: Keep reducers as pure as possible – reducers ideally should only update your current state, they shouldn’t be used to perform other side effects.
Following up on the above action example, the corresponding reducer would look like this:
With these concepts, you are ready to start using redux at its basic level.
Remember what I said about keeping reducers pure and not performing side effects? Well, what if you really have to perform a specific side effect? This is where the concept of middleware comes in. While the basic redux flow only allows you to dispatch actions, middleware allows you to dispatch functions or promises. This is useful for apps that need asynchronous data flow. I read this piece to understand more about how middleware works. With middleware, you can dispatch an action and also perform some other function. To explain it in simple terms, the middleware catches an action that has been dispatched before it gets to the reducer and performs some side effect, while passing the action down to the reducer using next(action).
For example, if we wanted to log the state before and after dispatching buttonClickedAction, we can define our middleware as such:
This middleware is then included when creating the store by calling applyMiddleware(loggingMiddleware).
Turns out that this was exactly what I needed to solve my not-so-little issue at work. I basically needed to perform some validation on a piece of data after submitting a form.
Learning all of this meant that one more thing was added to my growing tech stack. Now, on to the next – expanding my knowledge of React.