When React came out I was really amazed with its easiness of use and learning curve. When I first started learning React I wasn’t aware of the fact that React is just a library and not a fully fledged framework. I wasn’t aware that I would need to make all decisions about architecture and what exactly I would need to use in order to solve some common tasks. Other frameworks would give you solutions and architecture out of the box which is not the case with React lib. Yes, someone might say that I was naive and dumb but that naivety and my will to learn new things pushed me forward and made me a better developer and decision maker.

Everyone who enters the world of React will need to make those decisions in some point of time, no matter how small (HTTP client, routing, build tools and many more) or big they are (state management, architecture…). One particular decision that we almost always have to make when we’re starting with the development of an application is how to handle state in application. Choosing the best way to handle state is inevitable. The thing with JavaScript is that there is a double-digit number of potential solutions — which can be a good thing (you have plenty of options to choose from) or a bad thing (too many options can be really daunting and researching all of them takes up your precious time).

We are still in the chaotic phase of shifting paradigms in the frontend world, therefore new libraries for state management are emerging every day. In all this chaos one solution blazed out — enter Redux.

Growth of GitHub stars over time for Flux libraries (Redux skyrocketing)

I’m not here to promote Redux, but to explain to you why I think that you should learn Redux and how it can make you a better developer in general. Even if you have already chosen another lib for state management or you still don’t know what the concepts behind Redux are, I still recommend investing some time in learning Redux because it will pay off in the longer run. We will just scratch the surface of some concepts related to Redux which, in my opinion, every developer should know. If you want to find out more about these concepts you can find useful links at the end of the article.

For those who don’t know what Redux is:

Redux is a predictable state container for JavaScript apps.

It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test.

— official website

If you’re already familiar with it then let’s imagine that we don’t know anything about Redux and its underlying concepts, therefore our devSkills array is empty:

const devSkills = []

console.log( devSkills .length) // 0 :(

Don’t panic, we’re developers and we’re constantly evolving so let’s fill up this array with everything that we will learn if we choose to invest our time in Redux. We’ll start with bare bones and build up to the whole Redux.

Flux architecture

const devSkillsFlux = [… devSkills , 'Flux architecture']

Every house needs a strong foundation upon which it is built and state management libraries are the same way: they have an underlying architecture upon which they are based. You may be wondering why this is important to know and the answer is that once you get familiar with the architecture you will be able to apply it on other applications and you will be able to choose more wisely when you end up in a battle between Flux, MVC, MVVM or something else. Libraries and frameworks come and go but architectures like Flux are here to stay forever — and this is the precise reason to learn them.

Architecture wise, Redux is not that different from Flux and I consider it an improvement over Flux architecture. The key concept behind Flux/Redux architecture is strict unidirectional data flow. This means that all data in your application will follow the same lifecycle pattern which makes your app predictable and easier to understand.

A typical example of data flow would be:

User clicks on a button that will trigger the fetching of data from backend When you get a response back, you’ll dispatch an action with a plain object that has an action type and a payload property with your response store.dispatch({ type: ‘I_GOT_RESPONSE’, payload: response.data }) Store will see that you’ve dispatched an action and it will run reducer Reducer function will update store according to the action type After the state is updated, all the changes in store will automatically be reflected to UI components that are connected to store

Strict unidirectional data flow inside of Redux

Immutable data

const devSkillsImmutable = […devSkillsFlux, 'Immutable data']

With Redux comes the concept of immutable data because Redux and immutable data go together like bread and butter. The whole philosophy about immutable data is that once created, the object should never be changed. If you want to make any changes to object you should create a new object with changed properties. The use of immutable data will make your application more performant, easier to debug and easier to develop.

To understand immutable data you need to understand that in JavaScript non-primitive types (objects, arrays, functions…) are passed by reference and primitive types (string, number, boolean, symbol, null and undefined) are passed by value. This means that primitive types are immutable by default and you can’t change them. Instead, when you pass a primitive type to another variable, it will get a new copy of that value.

// Primitive types are immutable by default

let x = 25

let y = x

y = 100 console.log(x) // 25

console.log(y) // 100

On the other hand, when you pass a variable of non-primitive type as an object to another variable, they will both point/refer to the same object.

// Non-primitive types are mutable

let animal = {

name: 'Mouse'

} let anotherAnimal = animal

anotherAnimal.name = 'Elephant' console.log(animal) // {name: "Elephant"}

console.log(anotherAnimal) // {name: "Elephant"}

console.log(animal === anotherAnimal) // true

You may say, “Ok, I’ll use const instead of let or var and my object will be immutable”— not so fast, cowboy! Even with const you can mutate your object but you can’t reassign it:

const animal = { name: 'Mouse' } // This will work because we're not reassigning variable

animal.favoriteFood = 'Cheese' // This will fail

animal = { name: 'Elephant' }

So, how should you handle immutable data in JavaScript?

When you want to update an object you should create a completely new object, thus keeping it immutable. For that purpose you can use the Object.assign method or object spread syntax:

const animal = { name: 'Mouse' } // Object.assign

const anotherAnimal = Object.assign({}, animal, {

name: 'Elephant'

}) // Object spread operator

const yetAnotherAnimal = {

...animal,

name: 'Crocodile'

} console.log(animal) // {name: "Mouse"}

console.log(anotherAnimal) // {name: "Elephant"}

console.log(yetAnotherAnimal) // {name: "Crocodile"}

console.log(animal === anotherAnimal) // false

console.log(animal === yetAnotherAnimal) // false

When dealing with arrays you should not use methods that will mutate the given array such as push , shift , unshift , reverse , sort and other array methods. Instead, you should use their immutable equivalents. As you might have already noticed, in the beginning of every chapter I was using the spread operator instead of the push method to add a new string into our devSkills array.

const animals = ['Mouse', 'Elephant']

const animalsUpgrade = [...animals, 'Crocodile'] console.log(animals) // ["Mouse", "Elephant"]

console.log(animalsUpgrade) // ["Mouse", "Elephant", "Crocodile"]

console.log(animals === animalsUpgrade) // false

To make sure that you’re consistent with the immutability approach and that you’re not mutating your store object in Redux you can use react-state-invariant middleware that will spit out an error if you try to do some mutations in store. Also, Facebook’s Immutable.js library can help you keep your state immutable by providing persistent immutable data structures out of the box.

By virtue of using immutability approach your state will become more predictable, your application more performant and your code more testable and debuggable — which is something that we all want in the end!

Pure functions

const devSkillsPureFn = […devSkillsImmutable, 'Pure functions']

Along with immutable data, pure functions are one of the core concepts of functional programming. They find their way into the Redux as reducers. Pure functions are almost the same as every other function but with a few key differences:

given the same input it will always give the same output

produces no side effects

doesn’t rely on external state

Let’s take a look at an example of an impure and a pure function:

function impureAddFood (animal) {

animal.food = ['banana', 'pizza']

} function pureAddFood (animal) {

return {

...animal,

food: ['banana', 'pizza']

}

} const donkey = { name: 'Donkey' }

const monkey = { name: 'Monkey' } impureAddFood(donkey)

const monkeyWithFood = pureAddFood(monkey)

Now, if we log out our animals we can see that our variable donkey is mutated by impureAddFood function and monkey is unchanged.

console.log(donkey)

{ name: 'Donkey', food: ['banana', 'pizza']} console.log(monkey)

{ name: 'Monkey'} console.log(monkeyWithFood)

{ name: 'Monkey', food: ['banana', 'pizza']}

What I really like about pure functions is that they are easy to test, reusable and predictable. In Redux, reducers are pure functions and their task is to make updates to store — they will take the previous state together with an action and return the new state without any side effects.

Higher order functions

const devSkillsHOC = […devSkillsPureFn, 'Higher order functions']

There are two main reasons why you should learn higher-order functions!

Number one: there are many use cases for higher-order functions and they have particular significance in functional programming.

Number two: well, you will sound smart when you tell someone that you know what higher order functions are. The first reason in itself should be more than enough and the second one can be ignored because in reality, higher-order function is just a fancy expression for a function that accepts another function as an argument or one that returns a function as a result.

function makeNoise (animalName) {

return function (noise) {

console.log(`${animalName} says ${noise}`)

}

} const mouseNoise = makeNoise('Mouse') mouseNoise("rooooooooar, I'm a lion!!!")

// Mouse says rooooooooar, I'm a lion!!!

On the flip side, we have first order functions which don’t take a function as an argument or return function as a result. I’m pretty sure that you’ve already used higher-order functions; you just weren’t aware of it — for example: map, filter, some, every, find and reduce are all higher order functions. Once you include them into your arsenal you will never look back.

In Redux, you can split up reducer logic or customise reducer behaviour by using higher order functions where your function will accept a reducer function as an argument or will return a new reducer as a result.

Other Redux related stuff

const devSkillsGoku = […devSkillsHOC, 'other Redux related stuff']

Let’s see what we have learned so far:

console.log(devSkillsGoku)

[

'Flux architecture',

'Immutable data',

'Pure functions',

'Higher order functions',

'other Redux related stuff'

]

We scratched the surface of some of the main concepts that you will learn along with Redux. There is a lot more that you can learn on this Redux journey and become a better developer and decision maker. Take your time and learn how to make your application more predictable, testable, debuggable and clean.

The journey for more knowledge:

What was the best thing that you learned alongside Redux? Was it worth learning? Do you use some other state management library and which one would you recommend? Leave me a comment :)

Kudos to Vlado Kopić for reviewing and throwing his magic into this article :*