Learning Redux
Jacshuo Lv3

Redux Terminology

Actions and Action Creators

The only way for an application to change the state is by processing actions. In most cases, actions in Redux are nothing more than plain JavaScript objects passed to the store that hold all the information needed for the store to be able to modify the state:

1
2
3
4
5
6
7
{
type: 'INCREMENT',
payload: {
counterId: 'main',
amount: -10
}
}

Since these objects might have some logic and be used in multiple places in an application, they are commonly wrapped in a function that can generate the objects based on a parameter:

1
2
3
4
5
6
7
8
9
function incrementAction(counterId, amount){
return {
type: 'INCREMENT',
payload: {
counterId,
amount
}
};
};

As these functions create action objects, they are aptly named action creators.

Reducers

Once an action is sent to the store, the store needs to figure out how to change the state accordingly. To do so, it calls a function, passing it the current state and the received action:

1
2
3
4
function calculateNextState(currentState, action){
//...
return nextState;
}

This function is called a reducer. In real Redux applications, there will be one root reducer function that will call additional reducer functions to calculate the nested state.

1
2
3
4
5
6
7
8
function rootReducer(state,action){
switch (action.type){
case 'INCREMENT':
return { ...state, counter: state.counter + action.payload.amount };
default:
return state;
}
}

Reducers never modify the state; they always create a new copy with the needed modifications.

Middleware

Middleware is a more advanced feature of Redux and will be discussed in detail. The middleware act like interceptors for actions before the reach the store: they can modify the actions, create more actions, suppress actions, and much more. Since the middleware have access to the actions, the dispatch() function, and the store, they are the most versatile and powerful entities in Redux.

Store

Unlike many other Flux implementations, Redux has a single store that holds the application information but no user logic. The role of the store is receive actions, pass them through all the registered middleware, and then use reducers to calculate a new state and save it.
When it receives an action that causes a change to the state, the store will notify all the registered listeners that a change to the state has been made. This will allow various parts of the system, like the UI, to update themselves according to the new state.

General Concepts

Redux is about functional programming and pure functions. Understanding these concepts is crucial to understanding the underlying principles of Redux.

Functional programming has become a trendy topic in the web development domain lately, but it was invented around the 1950s. The paradigm centers around avoiding changing state and mutable data—in other words, making your code predictable and free of side effects.

JavaScript allows you to write code in a functional style, as it treats functions as first-class objects. This means you can store functions in variables, pass them as arguments to other functions, and return them as values of other functions. But since JavaScript was not designed to be a functional programming language per se, there are some caveats that you will need to keep in mind. In order to get started with Redux, you need to understand pure functions and mutation.

Pure and Impure Functions

A pure function returns values by using only its arguments: it uses no additional data and changes no data structures, touches no storage, and emits no external events (like network calls). This means that you can be completely sure that every time you call the function with the same arguments, you will always get the same result. Here are some examples of pure functions:

1
2
3
4
5
function square(x) { return x * x; }

Math.sin(y);

arr.map((item) => item.id);

If a function uses any variables not passed in as arguments or creates side effects, the function is impure. When a function depends on variables or functions outside of its lexical scope, you can never be sure that the function will behave the same every time it’s called. For example, the following are impure functions:

1
2
3
4
5
6
7
8
9
function getUser(userId) { 
return UsersModel.fetch(userId).then(
(result) => result
);
}

Math.random();

arr.map((item) => calculate(item));

Mutating Objects

Another important concept that often causes headaches for developers starting to work with Redux is immutability. JavaScript has limited tooling for managing immutable objects, and we are often required to use external libraries.

Immutability means that something can’t be changed, guaranteeing developers that if you create an object, it will have the same properties and values forever. For example, let’s declare a simple object as a constant:

1
2
3
4
5
const colors = {
red: '#FF0000',
green: '#00FF00',
blue: '#0000FF'
};

Even though the colors object is a constant, we can still change its content, as const will only check if the reference to the object is changed:

1
2
3
4
5
colors = {}; 
console.log(colors);

colors.red = '#FFFFFF';
console.log(colors.red);

Try writing this in the developer console. You will see that you can’t reassign an empty object to colors, but you can change its internal value.

To make the colors object appear immutable, we can use the Object.freeze() method:

1
2
3
4
5
Object.freeze(colors);

colors.red = '#000000';

console.log(colors.red);

The value of the red property will now be ‘#FFFFFF’. If you thought that the value should be ‘#FF0000’, you missed that we changed the red property before we froze the object. This is a good example of how easy it is to miss this kind of thing in real applications.

Here, once we used Object.freeze(), the colors object became immutable. In practice things are often more complicated, though. JavaScript does not provide good native ways to make data structures fully immutable. For example, Object.freeze() won’t freeze nested objects:

1
2
3
4
5
6
7
8
9
10
const orders = { 
bread: { price: 10 },
milk: { price: 20 }
};

Object.freeze(orders);

orders.milk.price -= 15;

console.log(orders.milk.price);

To work around the nature of our beloved language, we have to use third-party libraries like deepfreeze or ImmutableJS.

  • 本文标题:Learning Redux
  • 本文作者:Jacshuo
  • 创建时间:2019-07-21 17:05:07
  • 本文链接:https://blog.imade.life/2019/07/21/Learning-Redux/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!