在开始前,我们需要知道,在本文中将会是简单的应用Redux,一些复杂和高级阶段的API 可能不会出现,这可能需要自主学习
开始和安装
在项目中,当我们需要Redux时,我们需要使用 npm 安装 Redux 和 React绑定库,开发调试工具
npm install redux --save
npm install react-redux --save
npm install redux-devtools --save-dev
核心和概念
Action
把数据从应用传到stort的有效载体,它是store数据的唯一来源.Action 本质上是对象。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。同时建议使用单独的模块或文件来存放 action
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
由于 action 的数量众多,因此需要构建函数方法。 Action 创建函数 就是生成 action 的方法,只是简单的返回一个 action
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
// Redux 中只需把 action 创建函数的结果传给 dispatch() 方法即可发起一次 dispatch 过程
// dispatch(addTodo(text))
Reducer
Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state 当我们确定了 state 对象的结构,就可以开始开发 reducer。reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state
function todoApp(state = initialState, action) {
// 这里暂不处理任何 action,
// 仅返回传入的 state。
// initialState 可能就是state数据中的一个
return state
}
永远不要在 reducer 里做这些操作:
- 修改传入参数;
- 执行有副作用的操作,如 API 请求和路由跳转;
- 调用非纯函数,如 Date.now() 或 Math.random()。
Store
Store是存储数据的地方,它就像是一个容器。用来维持应用所有的 state 树 的一个对象,整个应用只有一个 Store。redux 提供 createStore(fn) 并接收一个函数来生成一个 Store,
import { createStore } from 'redux'
import todoApp from './reducers' //由下文中的Reducer集合文件
let store = createStore(todoApp)
//createStore() 的第二个参数是可选的, 用于设置 state 初始状态
let store = createStore(todoApp, window.STATE_FROM_SERVER)
Store的职责
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器;
- 通过 subscribe(listener) 返回的函数注销监听器。
store.dispatch()
dispactch接受一个action对象作为参数并发送出去。你可以在任何地方调用 store.dispatch(action);Store 会把两个参数传入 reducer: 当前的 state 树和 action 。Action 是把数据传入 store 的惟一途径,所以任何数据,无论来自 UI 事件,网络回调或者是其它资源如 WebSockets,最终都应该以 action 的形式被 dispatch
Redux 的基本原则
- 唯一的数据源
整个应用的 state 被储存在一棵 object tree 中,且这个 object tree 只存在唯一的 store
这让同构应用开发变得非常容易。来自服务端的 state 可以在无需编写更多代码的情况下被序列化并注入到客户端中。由于是单一的 state tree ,调试也变得非常容易。在开发中,你可以把应用的 state 保存在本地,从而加快开发速度。此外,受益于单一的 state tree ,以前难以实现的如“撤销/重做”这类功能也变得轻而易举
- State是只读的
唯一改变 state 的方法就是触发 action,通过 Reducer 对 state 进行修改
这样确保了视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。因为所有的修改都被集中化处理,且严格按照一个接一个的顺序执行,因此不用担心 race condition 的出现。 Action 就是普通对象而已,因此它们可以被日志打印、序列化、储存、后期调试或测试时回放出来
- 使用纯函数进行修改
Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务,如分页器
项目结构参考
src/
actions/ 【存储action创建函数的文件夹】
index.js
components/ 【存储展示组件的文件夹】
App.js
...
containers/ 【存储容器组件的文件夹】
Add.js
...
reducers/ 【存储reducer的的文件夹】
index.js
add.js
...
展示组件和容器组件的区分
展示组件 | 容器组件 | |
---|---|---|
作用 | 描述如何展示(骨架,样式) | 描述如何运行(数据获取,状态更新) |
直接使用redux | 否 | 是 |
数据来源 | props | 监听Redux state |
数据修改 | 从Props调用回调函数 | 向Redux 派发action |
...
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import rootReducer from './reducers'
import App from './components/App'
const store = createStore(rootReducer)
// Provider是React Redux 的高阶组件,用于将 Redux 绑定到 React
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
...
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import List from '../components/TodoList'
//
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed) //数组通过completed属性筛选
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
case 'SHOW_ALL':
default:
return todos
}
}
const mapStateToProps = function(state){
console.log(state)
return ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
})
}
const mapDispatchToProps = function(dispatch){
console.log('1')
return({
toggleTodo: id => dispatch(toggleTodo(id))
})
}
export default connect( //以注入对象的方式,以健取值
mapStateToProps, //注入todos
mapDispatchToProps
)(List)