1.概念
2.action
(1)本质是一个JS对象;
3.reducer
(1)Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
(2)接受action和当前参数state作为参数,然后返回一个新的state
const reducer = function (state, action) {
// ...
return new_state;
};
例子:
const defaultState = 0;
const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD':
return state + action.payload;
default:
return state;
}
};
const state = reducer(1, {
type: 'ADD',
payload: 2
});
实际应用中,Reducer 函数不用像上面这样手动调用,store.dispatch方法会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。
import { createStore } from 'redux';
const store = createStore(reducer);
createStore
接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch
发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State。
// State 是一个对象
function reducer(state, action) {
//后面的thingTochange会覆盖前面的state
return Object.assign({}, state, { thingToChange });
// 或者,对象展开符的对象合并,后面有的属性会覆盖前面的
return { ...state, ...newState };
}
// State 是一个数组
function reducer(state, action) {
//在数组里面添加一个项目,如果后面的也是一个数组,直接...new Item
return [...state, newItem];
}
(3)reducer是一个纯函数。
同样的输入必定导致同样的输出。
4.store
(1)createStore函数接受另一个函数作为参数,返回新生成的Store对象 。store对象包含所有数据。
(2)一个state对应一个view,只要state相同,view就相同,所以state的变化会导致view的变化。
用户接触不到state,只接触view,所以,state的变化是view导致的,action是view发出的通知,表示State应该要变化了。
action是一个对象,type属性必须,其他可以自由设置,一般会有payload action描述当前发生的事情,改变State的唯一办法,就是使用Action。
store.dispatch()是view发出action的唯一方法。
(3)
Store 允许使用store.subscribe
方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。
import { createStore } from 'redux'; const store = createStore(reducer); store.subscribe(listener);
显然,只要把 View 的更新函数(对于 React 项目,就是组件的render
方法或setState
方法)放入listen
,就会实现 View 的自动渲染。
store.subscribe
方法返回一个函数,调用这个函数就可以解除监听。
let unsubscribe = store.subscribe(() => console.log(store.getState()) ); unsubscribe();
5.react-redux
react项目中,可以直接使用redux,也可以使用react-redux,后者需掌握额外的API,并且遵循它的组件拆分规范。
(1)react-redux将所有组件分为:UI组件和容器组件
UI组件最大特征是没有this.state(无状态),只负责UI的呈现,不带有任何业务逻辑,不使用redux的API;
容器组件:负责管理数据和业务逻辑,不负责UI呈现,带有内部状态,使用redux的API。
使用react-redux,如果一个组件既有UI又有逻辑,那么将它拆分成下面结构:外面是一个容器组件,里面包含一个UI组件,前者负责与外部通信,将数据传给后者,由后者渲染出视图。
react-redux规定:所有UI组件都由用户提供,容器组件则由react-redux自动生成,也就是说,用户负责视觉层,状态管理则全部交给它。
(2)connect()
react-redux提供connect方法,用于从UI组件生成容器组件。connect的意思,就是将两者连起来。
import { connect } from 'react-redux'
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
mapStateToProps:输入逻辑,即将state
映射到 UI 组件的参数(props
)。作为函数,返回一个对象,里面的每一个键值对就是一个映射。
mapStateToProps
会订阅 Store,每当state
更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
mapStateToProps
的第一个参数总是state
对象,还可以使用第二个参数,代表容器组件的props
对象。
使用ownProps
作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
connect
方法可以省略mapStateToProps
参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
mapDispatchToProps()
mapDispatchToProps
是connect
函数的第二个参数,用来建立 UI 组件的参数到store.dispatch
方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
如果mapDispatchToProps
是一个函数,会得到dispatch
和ownProps
(容器组件的props
对象)两个参数。
mapDispatchToProps
作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
如果mapDispatchToProps
是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
(3)<Provider>组件
connect
方法生成容器组件以后,需要让容器组件拿到state
对象,才能生成 UI 组件的参数。
一种解决方法是将state
对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state
传下去就很麻烦。
React-Redux 提供Provider
组件,可以让容器组件拿到state
。
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp);
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。
6.问题E
(1)为什么叫做reducer?
因为它可以作为数组的reduce
方法的参数。
const actions = [
{ type: 'ADD', payload: 0 },
{ type: 'ADD', payload: 1 },
{ type: 'ADD', payload: 2 }
];
const total = actions.reduce(reducer, 0); // 3
(2)什么叫纯函数?
同样输入导致同样输出。
一些约束:
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用
Date.now()
或者Math.random()
等不纯的方法,因为每次会得到不一样的结果。
(3)redux怎么解决异步的问题?
Action 发出以后,Reducer 立即算出 State,这叫做同步;Action 发出以后,过一段时间再执行 Reducer,这就是异步。
依靠使用中间件(middleware)。
(4)Provider的原理
是React组件的context属性。
store放在了上下文对象context上面,然后子组件就可以从context拿到store。
Provider的唯一功能就是传入store对象。