Redux使用
基本概念
store
store 是应用状态 state 的管理者,包含下列四个函数:
1、getState() 获取整个 state
2、dispatch(action) 触发 state 改变的【唯一途径】
3、subscribe(listener) 订阅state改变
4、replaceReducer(nextReducer)
state
如何初始化?
如何获取state
var state = store.getState()
action
做什么用的?
是一个对象,里面包含type字段和payload,type是给Reducer修改state的时候判断用的,payload是要修改的数据
{
type: 'ADD_TODO',
payload: {
id: 1,
content: '待办事项1',
completed: false
}
}
action creator
负责复用action对象的,可以给payload赋值不同数据
function addTodo(content) {
return {
type: 'ADD_TODO',
payload: {
id: id++,
content: content, // 待办事项内容
completed: false // 是否完成的标识
}
}
}
action是可以异步的,如何异步呢?
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
export function increment() {
return {
type: INCREMENT_COUNTER
};
}
export function decrement() {
return {
type: DECREMENT_COUNTER
};
}
//这里正常来说如果不给redux添加中间件的话这样会报错,但是引入redux-thunk这个中间件,就可以改变原来的dispatch处理方式,支持返回函数并且进行异步处理,并且给返回的函数注入dispatch, getState
export function incrementIfOdd() {
return (dispatch, getState) => {
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
dispatch(increment());
};
}
export function incrementAsync(delay = 1000) {
return (dispatch) => {
setTimeout(() => {
dispatch(increment());
}, delay);
};
}
那么redux-thunk内部又是如何实现的呢?以下是源码分析:
//redux添加中间件源码
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
//这里负责将getState和dispatch注入
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
return enhancer(createStore)(reducer, preloadedState)
//redux-thunk源码
//传入一些额外的
function createThunkMiddleware(extraArgument) {
// 这段写法的意思是: 相当于函数柯里化将多个参数层层包装
// return function ({
// dispatch,
// getState
// }) {
// 根据上面redux源码 next就是 store.dispatch
// return function (next) {
// 这个时候实际返回的dispatch就被改写成这个了: 参考redux源码:dispatch = compose(...chain)(store.dispatch)
// return function (action) {
// 然后在这里传入action creator 就可以处理函数和对象两种情况下然后进行异步
// if (typeof action === 'function') {
// return action(dispatch, getState, extraArgument);
// }
// return next(action);
// }
// }
// }
return ({ dispatch, getState }) => next => action => {
//判断
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
reducer
作用:修改state
如何触发:用户每次 dispatch(action) 后,都会触发 reducer 的执行
reducer 的实质是一个函数,根据 action.type 来更新 state 并返回 nextState
最后会用 reducer 的返回值 nextState 完全替换掉原来的 state
注意:上面的这个 “更新” 并不是指 reducer 可以直接对 state 进行修改 Redux 规定,须先复制一份 state,在副本 nextState 上进行修改操作 例如,可以使用 lodash 的 cloneDeep,也可以使用 Object.assign / map / filter/ ... 等返回副本的函数
Reducer 必须是同步的纯函数
具体使用
如何将react和redux结合起来?
需要react-redux
react-redux模块用来做什么用?
redux是一个独立的库,这个库其实可以用在angular,vue等前端框架中,所以要使用在react中需要通过react-redux做一个转换
react-redux提供了三个api
connect()
这个方法用来负责将react组件和redux store连接起来
通过什么样子的方式把这两个连接起来?
以下是connect方法需要传入的参数
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)
mapStateToProps:将redux中的state映射到props中去
示例:
//这一段是
export function increment() {
return {
type: INCREMENT_COUNTER
};
}
export function decrement() {
return {
type: DECREMENT_COUNTER
};
}
export function incrementIfOdd() {
return (dispatch, getState) => {
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
dispatch(increment());
};
}
export function incrementAsync(delay = 1000) {
return (dispatch) => {
setTimeout(() => {
dispatch(increment());
}, delay);
};
}
//组件映射代码
import { bindActionCreators } from 'redux';
//映射state的某些字段到props中
function mapStateToProps(state) {
return {
counter: state.counter
};
}
//映射dispatch到组件的props
function mapDispatchToProps(dispatch) {
return bindActionCreators(CounterActions, dispatch);
}
//将组件传入其中映射
export default connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
//组件内部调用
const {
increment,
incrementIfOdd,
incrementAsync,
decrement,
counter
} = props;
以上源码中存在一个bindActionCreators函数,这个函数来自redux函数库
以下通过源码讲解一下干什么用的
function bindActionCreators(actionCreators, dispatch) {
//如果传入的是一个函数则调用bindActionCreator函数进行包装,这样返回给组件的函数只要调用就可以触发state更新
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
//如果是一个对象,就遍历键值进行包装然后返回组合好的对象
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
//包装action creator函数为直接使用dispatch触发更改的函数
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
根据源码来看,这个函数的作用就是将dispatch和actionCreator包装成一个接受单一参数触发更新store的方法,简化书写,也就是函数的柯里化的应用。
Provider
这个标签使得经过connect函数包装的组件可以获得store
正常来说,一个被connect包装过的组件只能用在里面
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { App } from './App'
import createStore from './createReduxStore'
const store = createStore()
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
connectAdvanced
它是一个将 React 组件连接到 Redux store 的函数。这个函数是 connect() 的基础,但是对于如何把state, props, 和 dispatch 组合到最后的 props 中,则不那么自以为是。它不对默认值或结果的记录做任何假设,而是将这些责任留给调用者。
意思就是不会提供默认组合的方法,而是把这些方法让开发者实现,实现更灵活的扩展
总结
参考
github.com/kenberkeley…
react-redux.js.org/
www.redux.org.cn/