原文链接:https://medium.freecodecamp.org/a-quick-guide-to-redux-for-beginners-971d18c0509b
译者注: 在翻译时,固有名词不做翻译.举例(redux,react,store(存储),action(动作),reducer(减速器?),dispatch(调度),props等)
初学者的Redux快速指南
Redux是一个通常与React一起使用的状态管理器。 它不受该库的限制,也可以与其他技术一起使用。 但为了这篇文章,我们会坚持使用React。
React有自己的方式来管理状态,你可以阅读我的介绍React Beginner’s Guide(译者注:可能需要科学上网),在React中管理状态。
那篇文章中我没有提到的是,我所讨论的方法并没有扩展。
在简单的情况下使用props在组件树之间传递状态,但在一个复杂的应用程序中,您可能会发现使用props上下传递了几乎所有的状态。 更好的方法是使用一个外部的全局的store。
Redux是一种管理应用程序状态的方式。
有几个概念需要掌握,但是一旦你掌握了它们,Redux就是解决这个问题的一个非常简单的方法。
只需再次注意:Redux在React应用程序中非常受欢迎,但它对于React并不是唯一的。 几乎每个流行的框架都有整合。 也就是说,我会用React分享一些例子,因为它很常见。
什么时候应该使用Redux
Redux是大中型应用程序的理想选择。 只有当您在使用React的默认状态管理(或您使用的任何其他库)管理状态遇到麻烦的时候才应该使用它。
简单的应用程序根本不需要它(简单的应用程序使用也没有什么问题)。
不可变状态树
在Redux中,应用程序的整个状态都是由一个JavaScript对象表示的,称为状态树。
我们称之为不可变状态树,因为它是只读的:不能直接更改。
它只能通过dispatch一个action来改变。
Actions
一个Action是一个JavaScript对象,用极简的方式描述一个变化(只是需要的信息):
{
type: 'CLICKED_SIDEBAR'
}
// e.g. with more data
{
type: 'SELECTED_USER',
userId: 232
}
action对象的唯一要求是要有一个type属性,其值通常是一个字符串。
Action的type应该是常量
在一个简单的应用程序中,一个动作类型可以被定义为一个字符串(就像我在前面的文章中所做的那样)。
当应用程序增长时,最好使用常量:
const ADD_ITEM = 'ADD_ITEM'
const action = { type: ADD_ITEM, title: 'Third item' }
并在对应的文件中分离actions,然后导入它们:
import { ADD_ITEM, REMOVE_ITEM } from './actions'
Action的生成器
actions生成器是创建actions的函数。
function addItem(t) {
return {
type: ADD_ITEM,
title: t
}
}
通常将action生成器与触发dispatcher(调度程序)结合使用:
dispatch(addItem('Milk'))
或者定义一个action dispatcher函数:
const dispatchAddItem = i => dispatch(addItem(i)) dispatchAddItem('Milk')
Reducers
当一个action被触发时,一定发生一些事情,并且应用程序的状态一定改变了。
这是reducers(减速器? 译者注:这个概念的应参考原生js的Array.Prototype.reduce)的作用。
什么是Reducer?
reducer是一个纯函数,它根据之前的状态树和dispatch(调度)的action(动作)来计算下一个状态树。
(currentState, action) => newState
一个纯函数接受一个输入并返回一个输出而不改变输入或其他的东西。 因此,reducer将返回一个全新的状态树对象,替换前一个。
Reducer不应该做什么?
由于reducer是一个纯函数,因此不该做:
- 改变他的参数
- 改变他的state-它应该改为用object.assign({},...)创建一个新的状态。
- 产生副作用(没有API调用改变任何东西)
- 调用非纯函数,这些函数是根据输入以外的因素(例如,Date.now()或者Math.random())改变其输出的函数。
虽然这不是强制规定,但你应该遵守这个规范;.
多个Reducers
由于一个复杂的应用程序的状态可能真的很广泛, 没有一个reducer可以应对多种不同的action,因此需要多个reducer。
实现一个reducer
在其核心上,Redux可以通过这个模型进行简化:
state(状态)
{
list: [
{ title: "First item" },
{ title: "Second item" },
],
title: 'Grocieries list'
}
actions列表
{ type: 'ADD_ITEM', title: 'Third item' }
{ type: 'REMOVE_ITEM', index: 1 }
{ type: 'CHANGE_LIST_TITLE', title: 'Road trip list' }
state的每一部分的reducer
const title = (state = '', action) => {
if (action.type === 'CHANGE_LIST_TITLE') {
return action.title
} else {
return state
}
}
const list = (state = [], action) => {
switch (action.type) {
case 'ADD_ITEM':
return state.concat([{ title: action.title }])
case 'REMOVE_ITEM':
return state.map((item, index) =>
action.index === index
? { title: item.title }
: item
default:
return state
}
}
全部state的reducer
const listManager = (state = {}, action) => {
return {
title: title(state.title, action),
list: list(state.list, action),
}
}
Store
store是一个对象:
- 保存整个APP的state(状态)
- 通过getState() 暴露状态
- 允许通过dispatch()更新状态
- 允许通过subscribe ()来注册(或取消注册)一个state改变的监听器
Store在整个APP中是唯一的.
以下是如何创建listManager APP 的store:
import { createStore } from 'redux'
import listManager from './reducers'
let store = createStore(listManager)
我可以使用服务器端数据初始化store吗?
当然可以, 传递一个初始状态就行:
let store = createStore(listManager, preexistingState)
获得state
store.getState()
更新state
store.dispatch(addItem('Something'))
监听state变化
const unsubscribe = store.subscribe(() =>
const newState = store.getState()
)
unsubscribe()
数据流
Redux中的数据流始终是单向的。
您在Store上调用dispatch(),传递一个Action。
store负责将action传递给reducer,产生下一个state。
store更新state并提醒所有监听者。