严格的单向数据流是 Redux 架构的设计核心,使用 Redux 的一个好处就是让 state 的变化过程变的可预知和透明。
Redux约定使用普通对象object tree来描述应用的 state ,如果想更新 state 中的数据,需要先发起一个 action请求来对更改进行描述,然后调用 reducer函数执行state更改。
Store 就是用来维持应用所有的 state 的一个对象。
getState()
返回应用当前的 state 树;
dispatch(action : object)
:
分发 action 改变 store;
subscribe(listener : Function)
:
注册监听器, 每当 dispatch action 的时候就会执行; state 树中的一部分可能已经变化, 可以在回调函数里调用 getState()
来拿到当前 state。
replaceReducer(nextReducer : Function)
:
用 nextReducer 替换当前 store 的 reducer。在需要实现代码分隔,而且需要立即加载一些 reducer 的时候才可能会用到它,在实现 Redux 热加载机制的时候也可能会用到。
创建一个 Redux store 来以存放应用中所有的 state:createStore(reducer, [preloadedState], enhancer)
参数
reducer(): Function
接收两个参数,分别是当前的 state 树和要处理的action,返回新的state 树。
preloadedState: any
初始化state。 如果使用 combineReducers 创建 reducer,它必须是一个普通对象,与传入的 keys 保持同样的结构;否则可以自由传入任何 reducer 可理解的内容。
enhancer: Function
Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。
返回值
Store
: 保存了应用所有 state 的对象。
栗子
import React from 'react'
import { createStore } from 'redux'
import reducer from './reducers'
const store = createStore(reducer);
Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。action 内必须使用一个字符串类型的 type
字段来表示将要执行的动作。
type
会被定义成字符串常量。{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Action Creator 就是生成 action 的方法。
function fetchPosts(url) {
return {
type: FETCH_POSTS,
url
}
}
store 里直接通过 store.dispatch()
调用 action,但是多数情况下会使用 react-redux 提供的 connect()
来调用。bindActionCreators()
可以自动把多个 action 创建函数 绑定到 dispatch()
方法上。
一个约定俗成的做法是通过创建函数生成 action 对象,而不是在dispatch 的时候内联生成action。
// 1. 内联生成 action
store.dispatch({
type: ADD_TODO,
text: 'Build my first Redux app'
})
// 2. Action Creator 生成 action
function fetchPosts(url) {
return {
type: FETCH_POSTS,
url
}
}
store.dispatch(fetchPosts(url))
Redux 的应用程序中最常见的 state 结构是一个简单的 JavaScript 对象,它最外层的每个 key 中拥有特定域的数据。给这种 state 结构写 reducer 的方式是分拆成多个 reducer,拆分之后的 reducer 都是相同的结构(state, action),并且每个函数独立负责管理该特定切片 state 的更新。多个拆分之后的 reducer 可以响应一个 action,在需要的情况下独立的更新他们自己的切片 state,最后组合成新的 state。
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
});
default:
return state;
}
}
这是一个高阶 Reducer ,combineReducers
接收拆分之后的 reducer 函数组成的对象,并且创建出具有相同键对应状态对象的Reducer函数。目前 combineReducers
只能处理普通的 JavaScript 对象。
如果没有给 createStore
提供预加载 state,输出 state 对象的 key 将由输入的拆分之后 reducer 组成对象的 key 决定。
结合Immutable.js 使用
combineReducers 不解决 Immutable.js,Maps等构建的 state tree,也不会把其余部分的 state 作为额外参数传递给 reducer 或者排列 reducer 的调用顺序,它同样不关心 reducer 如何工作。现在有大量提供类似功能的工具,例如 redux-immutable,这个第三方包实现了一个能够处理 Immutable Map 数据而非普通的 JavaScript 对象的 combineReducers
。
栗子
1.定义reducers
/**
* reducers.js
*/
function info(state = {}, action) {
switch (action.type) {
case 'UPDATE_NAME':
return { ...state, name: action.text };
case 'UPDATE_SCHOOL':
return { ...state, school: action.text };
default:
return state
}
}
function age(state = {}, action) {
switch (action.type) {
case 'INCREMENT_AGE':
return { ...state, age: state.age + 1 };
case 'DECREMENT_AGE':
return { ...state, age: state.age - 1 }
default:
return state
}
}
export { info, age };
2.创建store, 引入combineReducers
/**
* App.js
*/
import { createStore, combineReducers } from 'redux'
import { info, age } from './reducers.js'
let store = createStore(combineReducers({ info, age }), {
name: '',
school: '',
age: 0
});
console.log(store.getState())
// {
// info: { name: '', school: '' },
// age: 0
// }
store.dispatch({
type: 'UPDATE_NAME',
text: 'Use Redux'
})
console.log(store.getState())
// {
// info: { name: 'Use Redux', school: '' },
// age: 0
// }
同步: Action 发出以后,Reducer 立即算出 State;
异步:Action 发出以后,过一段时间再执行 Reducer;
默认情况下,createStore()
所创建的 Redux store 只支持同步数据流, dispatch
只能接收一个普通对象。怎么才能在异步操作结束后Reducer自动执行呢?这时就需要使用中间件。
Redux middleware 提供的是 action 被发起之后,到达 reducer 之前的扩展点,在每个 action 对象 dispatch 出去之前,注入一个自定义的逻辑来解释 action 对象。
中间件可以进行日志记录、创建崩溃报告、调用异步接口或者路由等等;在applyMiddleware
方法里把中间件作为参数传入。此方法是 Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。
applyMiddleware(...middleware)
参数
...middleware
遵循 Redux middleware API 的函数。每个 middleware 接受 Store
的 dispatch
和 getState
函数作为命名参数,并返回一个函数。该函数会被传入被称为 next
的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action)
,或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 dispatch
方法作为 next
参数,并借此结束调用链。所以,middleware 的函数签名是 ({ getState, dispatch }) => next => action
。
返回值
(Function) :一个应用了 middleware 后的 store enhancer。这个 store enhancer 的签名是 createStore => createStore
,但是最简单的使用方法就是直接作为最后一个 enhancer
参数传递给 createStore()
函数。
实现请求前后打日志, 请求失败处理异常的中间件:
import { createStore, combineReducers, applyMiddleware } from 'redux';
import reducers from './reducers'
// 请求前后日志记录middleware
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
// 请求异常记录middleware
const crashReporter = store => next => action => {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}
// createStore引入自己创建的middleware
const store = createStore(
combineReducers(reducers),
preloadedState,
applyMiddleware(logger, crashReporter)
)
从右到左来组合多个函数: compose(...functions)
compose(funcA, funcB, funcC)
形象为 compose(funcA(funcB(funcC())))
参数
(arguments): 需要合成的多个函数。预计每个函数都接收一个参数。它的返回值将作为一个参数提供给它左边的函数,以此类推
返回值
(Function): 从右到左把接收到的函数合成后的最终函数。
react-redux是使用redux开发react时使用的一个插件。react-redux提供了两个重要的API:Provider、connect
。
<Provider>
API<Provider>
使组件层级中的 connect()
方法都能够获得 Redux store。
正常情况下,你的根组件应该嵌套在 <Provider>
中才能使用 connect()
方法。如果不想把根组件嵌套在 <Provider>
中,你可以把 store
作为 props 传递到每一个被 connect()
包装的组件。
属性
store
: 应用程序中唯一的 Redux store 对象;children: ReactElement
: 组件层级的根组件;栗子
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
connect
API连接 React 组件与 Redux store。连接操作不会改变原来的组件类, 返回一个新的已与 Redux store 连接的组件类。connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
参数
mapStateToProps(state, [ownProps]): Function
stateProps。
mapStateToProps
函数如果定义了就会被调用;ownProps
,则该参数的值为传递到组件的 props。只要组件接收新的 props,mapStateToProps
也会被调用。(例如,当父组件重新渲染子组件props改变,那么 ownProps 参数,mapStateToProps 都会被重新计算)该函数必须返回一个纯对象,这个对象会与组件的 props 合并。
mapDispatchToProps(dispatch, [ownProps]):(Object / Function)
dispatchProps。
dispatch
方法会将 action creator 的返回值作为参数执行。这些属性会被合并到组件的 props 中。dispatch
函数,返回值是一个对象。返回对象通过 dispatch
函数与 action creator 以某种方式绑定在一起。mapDispatchToProps
,默认情况下,dispatch
会注入到你的组件 props 中。ownProps
,该参数的值为传递到组件的 props,而且只要组件接收到新 props,mapDispatchToProps
也会被调用。mergeProps(stateProps, dispatchProps, ownProps): Function]
props。
mapStateToProps()
与 mapDispatchToProps()
的执行结果和组件自身的 props
将传入到这个回调函数中。(Object.assign({}, ownProps, stateProps, dispatchProps)
的结果。options: Object
如果指定这个参数,可以定制 connect 的行为;
pure = true
] (Boolean): 如果为 true,connector 将执行 shouldComponentUpdate
并且浅对比 mergeProps
的结果,避免不必要的更新,前提是当前组件是一个“纯”组件,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true
。withRef = false
] (Boolean): 如果为 true,connector 会保存一个对被包含的组件实例的引用,该引用通过 getWrappedInstance()
方法获得。默认值为 false
。返回值
根据配置信息,返回一个注入了 state 和 action creator 的 React 组件。
redux-thunk是redux解决异步的中间件, 可以让 Action Creator 返回函数(普通的 Action Creator 默认返回一个对象)。如果action creator 返回的是一个函数,就执行它,如果不是,就按照原来的next(action)执行。
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';
const store = createStore(
reducer,
applyMiddleware(thunk)
);
redux-thunkexport default
的是createThunkMiddleware()
,这个函数返回的是一个柯里化过的函数。
function createThunkMiddleware(extraArgument) {
return function({ dispatch, getState }) {
return function(next){
return function(action){
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
}
}
栗子
fetchPosts
返回了一个函数,而普通的 Action Creator 默认返回一个对象。dispatch
和getState
,而普通的 Action Creator 的参数是 Action 的内容。requestPosts(title)
,表示操作开始。const fetchPosts = title => (dispatch, getState) => {
dispatch(requestPosts(title));
return fetch(url)
.then(response => response.json())
.then(json => dispatch(receivePosts(title, json)));
};
};
store.dispatch(fetchPosts('test'));
既然使用redux-thunk中间件可以让 Action Creator 返回函数,当然也可以返回其他值。另一种异步操作的解决方案是让 Action Creator 返回一个 Promise 对象。这就需要使用redux-promise
中间件。
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import reducer from './reducers';
const store = createStore(
reducer,
applyMiddleware(promiseMiddleware)
);
栗子
const fetchPosts = (dispatch, title) => new Promise((resolve, reject)=> {
dispatch(requestPosts(title));
return fetch(url).then(response => {
type: 'FETCH_POSTS',
payload: response.json()
});
});
Redux 让状态管理变得很冗长,大量的action、actionCreator、reducer让开发者不断在写重复的代码。redux-actions就解决了这个问题,让编写redux状态管理变得简单起来。
主要API有createAction(s)
、handleAction(s)
、combineActions
。
创建一个action: createAction(type)
import { createAction } from 'redux-actions';
export const increment = createAction('INCREMENT');
export const decrement = createAction('DECREMENT');
increment(); // { type: 'INCREMENT' }
decrement(); // { type: 'DECREMENT' }
increment(10); // { type: 'INCREMENT', payload: 10 }
decrement([1, 42]); // { type: 'DECREMENT', payload: [1, 42] }
创建多个action: createActions(actionMap, ...identityActions[, options])
第一个参数 actionMap 是一个对象,以 action type 为键名,键值value有三种形式:
action
创建的时候传入的参数,返回结果会作为到生成的action
的payload
的value。createActions({
ADD_TODO: todo => ({ todo }), // payload creator
REMOVE_TODO: [
todo => ({ todo }), // payload creator
(todo, warn) => ({ todo, warn }) // meta
]
});
处理action
,返回一个reducer
,处理一种类型的action type
。
处理一个action: handleAction(type, reducer, defaultState)
import { handleAction } from 'redux-actions';
handleAction(
'APP/COUNTER/INCREMENT',
(state, action) => ({
counter: state.counter + action.payload.amount
}),
defaultState
);
处理多个action: handleActions(reducerMap, defaultState[, options])
const reducer = handleActions(
{
INCREMENT: (state, action) => ({
counter: state.counter + action.payload
}),
DECREMENT: (state, action) => ({
counter: state.counter - action.payload
})
},
{ counter: 0 }
);
React Developer Tools、Redux DevTools 可以给开发人员在研发阶段调试程序带来极大的方便。 但是上了生产环境后,应该将禁止 DevTools。
Redux DevTools的作者已经给出了标准的解决方案。具体实现步骤如下:
process.env.NODE_ENV = JSON.stringify('production')
redux-devtools-extension/developmentOnly
引入方法import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
const store = createStore(
rootReducer,
composeWithDevTools(middlewareEnhancer)
);
本文学习的内容主要如下所示: 1.关闭按钮 2.Carets 3.快速设置浮动 4.内容区域...
不知道从什么时候开始,在网络上到处可以看到div+css,到底什么是div+css呢?难...
content [Ctrl+A 全选 注: 引入外部Js需再刷新一下页面才能执行 ] 终于见识了这...
链接可以制作 WML 卡片来显示 WML 的锚功能,图像可以制作 WML 卡片来显示图像. ...
下面介绍一下div嵌套div时margin不起作用的解决方案。 顺便科普下margin的定义和...
效果图: 整体效果: 视频加载: 拍照: 第一步:创建HTML元素 首先,我们要创建...
官方文档 https://developers.weixin.qq.... index.wxml view class="container"...
通过 display:bolck/none 完成一个菜单栏的效果 图1: 首先下面是已经完成的一...
Dreamweaver中的代码想要插入注释,该怎么添加呢?下面我们就来看看详细的教程。...
运维工程师这个岗位不同于后端开发岗位,到底运维工程师平时做什么? 老司机告诉...