Redux
Redux est un conteneur d'état JavaScript qui fournit une gestion d'état prévisible.
installation
npm install --save redux
Point
Tous les états de l'application sont stockés dans un seul magasin sous la forme d'une arborescence d'objets . La seule façon de changer l'état est de déclencher une action , un objet qui décrit ce qui se passe. Afin de décrire comment les actions modifient l'arborescence d'état, vous devez écrire des réducteurs .
exemple
import {
createStore } from 'redux';
/*
这是一个reducer,形式为 (state, action) => state 的纯函数
描述了 action 如何把 state 转变为下一个state
state 的形式,可以是基本类型/数组/对象 等等
唯一的要点是 当 state 变化时需要返回全新的对象,而不是修改传入的参数
*/
function counter (state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// 创建 Redux store 存放应用的状态
// API 是 { subscribe, dispatch, getState }
let store = createStore(counter)
// 可以手动订阅更新,也可以事件绑定到视图层
store.subscribe(() =>
console.log(store.getState())
)
// 改变内部 state 唯一的方法是 dispatch 一个 action
// action 可以被序列化,用日记记录和存储下来,后期可以以回放的方式执行
store.dispatch({
type: 'INCREMENT' }) // 1
store.dispatch({
type: 'INCREMENT' }) // 2
store.dispatch({
type: 'DECREMENT' }) // 1
Concept clé
Lors de l'utilisation d'objets ordinaires pour décrire l'état de l'application. Par exemple, l'état d'une application todo peut ressembler à ceci:
{
todos: [{
text: 'Eat food',
completed: true
},
{
text: 'Exercise',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}
Cet objet est comme un "Model", la différence est qu'il n'a pas de setter (méthode de modificateur). Par conséquent, les autres codes ne peuvent pas être modifiés à volonté, ce qui entraîne des bogues difficiles à reproduire.
Pour mettre à jour les données dans l'état, vous devez lancer une action. L'action est juste un objet JavaScript normal (remarquez, il n'y a pas de magie ici?) Pour décrire ce qui s'est passé. Voici quelques exemples d'actions:
{
type: 'ADD_TODO', text: 'Go to swimming pool' }
{
type: 'TOGGLE_TODO', index: 1 }
{
type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
Forcer l'utilisation d'actions pour décrire les avantages de tous les changements, c'est savoir clairement ce qui se passe dans l'application. Si quelque chose change, vous pouvez savoir pourquoi. Les actions sont comme des indicateurs décrivant ce qui s'est passé. Enfin, pour lier action et état, développer certaines fonctions, c'est réducteur. Encore une fois, sans aucune magie, le réducteur est juste une fonction qui reçoit l'état et l'action, et renvoie le nouvel état.
Pour les grandes applications, il est impossible d'écrire une seule de ces fonctions, nous écrivons donc de nombreuses petites fonctions pour gérer une partie de l'état séparément:
function visibilityFilter(state = 'SHOW_ALL', action) {
if(action.type === 'SET_VISIBILITY_FILTER') {
return action.filter;
} else {
return state;
}
}
function todos(state = [], action) {
switch(action.type) {
case 'ADD_TODO':
return state.concat([{
text: action.text, completed: false }])
case 'TOGGLE_TODO':
return state.map((todo, index) =>
action.index === index ?
{
text: todo.text, completed: !todo.completed } : todo
)
default:
return state;
}
}
Développez ensuite un réducteur pour appeler ces deux réducteurs afin de gérer l'état de l'ensemble de l'application:
function todoApp(state = {
}, action) {
return {
todos: todos(state.todos, action),
visibilityFilter: visibilityFilter(state.visibilityFilter, action)
}
}
Trois principes
Source de données unique
L'application entière state
est stockée dans une arborescence d'objets, et cette arborescence d'objets n'existe que dans la seule store
.
console.log(store.getState());
/* 输出
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
*/
L'état est en lecture seule
La seule façon de changer d'état est de déclencher action
. Action est un objet ordinaire utilisé pour décrire les événements qui se sont produits.
Cela garantit que ni la vue ni la requête réseau ne peuvent modifier directement l'état, au contraire ils ne peuvent qu'exprimer l'intention de la modification. Parce que toutes les modifications sont traitées de manière centralisée et exécutées dans un ordre strict les unes après les autres. Les actions ne sont que des objets ordinaires, elles peuvent donc être imprimées, sérialisées, stockées, post-déboguées ou relues pendant les tests.
store.dispatch({
type: 'COMPLETE_TODO',
index: 1
})
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
})
Utilisez des fonctions pures pour effectuer des modifications
Afin de décrire comment l'action modifie l'arborescence d'état, vous devez écrirereducers
Le réducteur n'est que quelques fonctions pures, il reçoit l'état et l'action précédents, et renvoie le nouvel état. Au début, vous ne pouvez avoir qu'un seul réducteur. Au fur et à mesure que l'application s'agrandit, vous pouvez la scinder en plusieurs réducteurs plus petits et faire fonctionner différentes parties de l'arborescence d'états indépendamment. Les réducteurs n'étant que des fonctions, vous pouvez contrôler l'ordre dans lequel ils sont appelés. Transmettez des données supplémentaires.
function visibilityFilter(state = 'SHOW_ALL', action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch(action.type) {
case 'ADD_TODO':
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
return state.map((todo, index) => {
if(index === action.index) {
return Object.assign({
}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}
import {
combineReducers, createStore } from 'redux'
let reducer = combineReducers({
visibilityFilter, todos })
let store = createStore(reducer)