原文地点:https://gmiam.com/post/react-…
没想到这篇文章这么晚才出,近来发生了太多的事变,已致于心态全无,终究也离开了如今的公司,没想到是这么的狼狈
一个人的光泽能够放到很大也能够小到微不足道,假如不能好好的计划终究也只能默默的蒙受
世上没有雷同的感同身受,感觉实在才真正的认清统统
好了,下面步入正题
前面看到 Flux 架构相对来说照样比较烦琐,同时社区也出现了许多第三方的框架情势,而 Redux 则脱颖而出
React 以组件的情势保护了一颗 UI 树,然则对状况数据没有做更多的处置惩罚,Redux 则把状况数据也笼统成了一棵树来保护
它本身与 React 没有直接关系,能够与其他框架合营运用,也能够很好的与 React 合营运用
Redux 的代码量异常短小,中心只供应了 5 个 API
createStore
combineReducers
bindActionCreators
applyMiddleware
compose
下面先来直观的感觉下 Redux
import { createStore } from 'redux';
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
let store = createStore(counter);
store.subscribe(() =>
console.log(store.getState())
);
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
表象能够看出进口是 createStore,吸收一个函数(这里叫做 reducer),这个函数吸收 state 与 action 俩个参数,然后 dispatch 一个对象(这里叫 action ,要包括一个 type 属性标明行动),reducer 函数就会被触发实行来操纵状况,同时也会触发 subscribe 定阅的回调,回调能够经由过程 store.getState() 猎取当前状况数据
到这里都很简朴,那末假如我们须要处置惩罚的数据和状况越来越多 reducer 函数就会越来越大致使难以保护,所以 Redux 供应了 combineReducers 来处置惩罚这类状况,它把这个大的 reducer 剖析成一个个小的 reducer ,每一个小 reducer 保护本身的状况数据,如许就剖析出了一个状况树
做下变种
reducers/todos.js
export default function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}
reducers/counter.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
reducers/index.js
import { combineReducers } from 'redux'
import todos from './todos'
import counter from './counter'
export default combineReducers({
todos,
counter
})
App.js
import { createStore } from 'redux'
import reducer from './reducers/index'
let store = createStore(reducer)
console.log(store.getState())
// {
// counter: 0,
// todos: []
// }
store.dispatch({
type: 'ADD_TODO',
text: 'Use Redux'
})
console.log(store.getState())
// {
// counter: 0,
// todos: [ 'Use Redux' ]
// }
能够看到我们应用 combineReducers 把 reducer 做了拆分,combineReducers 部份精简源码
export default function combineReducers(reducers) {
var reducerKeys = Object.keys(reducers)
var finalReducers = {}
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
var finalReducerKeys = Object.keys(finalReducers)
return function combination(state = {}, action) {
var hasChanged = false
var nextState = {}
for (var i = 0; i < finalReducerKeys.length; i++) {
var key = finalReducerKeys[i]
var reducer = finalReducers[key]
var previousStateForKey = state[key]
var nextStateForKey = reducer(previousStateForKey, action)
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
能够看到就是把对象中的 reducer 悉数实行一遍,把上次的状况传入进去,最新的状况返回返来,固然你也能够供应本身的
combineReducers 要领
前面我们注重到 store.dispatch 都是一个纯对象,也就是说我们的触发都是同步的,怎样支撑异步?
下面我们来引入 Redux 中心件来加强下
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
function increment() {
return {
type: 'INCREMENT_COUNTER'
}
}
function incrementAsync() {
return dispatch => {
setTimeout(() => {
dispatch(increment());
}, 1000)
}
}
const store = createStore(
rootReducer,
applyMiddleware(thunk)
)
store.dispatch(increment()) // 同步
store.dispatch(incrementAsync()) // 异步
同步体式格局的触发跟之前是一样的,这里的异步支撑就是靠 Redux 的 applyMiddleware 中心件情势与 thunk 中心件做加强支撑的
来看下 applyMiddleware 与部份 createStore 源码
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
return enhancer(createStore)(reducer, preloadedState)
}
....
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
createStore 里所谓的加强就是 applyMiddleware 一些中心件,
const store = createStore( rootReducer, applyMiddleware(thunk) )
与下面写法是等效的
const store = applyMiddleware(thunk)(createStore)(rootReducer)
看上面 applyMiddleware 的源码能够晓得会先用 createStore 建立原始 store,然后把 getState 与 dispatch 传给中心件,中心件处置惩罚完后返回扩大后的 store
看下 thunk 中心件源码
function createThunkMiddleware(extraArgument) {
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;
很简朴传入 dispatch, getState 后返回 next => action = > {…},然后传入 store.dispatch 返回 action => {…} 即扩大后的 dispatch
这个新的 dispatch 也是接收 action,假如是对象用原始 store.dispatch 直接触发,假如是函数则把 dispatch 传进函数体,把控制权交给函数内部
注重背面实行用到的 dispatch 已经是扩大后的能处置惩罚函数的 dispatch
回过头来在说下 compose API,applyMiddleware 能够接收一系列中心件,内部挪用 compose 来做处置惩罚
compose(…chain) 等同于 (…args) => f(g(h(…args)))
也就是说传入一组函数,它会倒序实行,把前一个的实行结果传给下一个,到达渐进加强结果
说到这里 Redux 和 它的 API 终究引见差不多了,至于 bindActionCreators 背面引见
说了这么多能够看到 Redux 本身就能够跑,那怎样与 React 结合起来?那就须要 react-redux 这个中心桥梁了
react-redux 供应了俩个 API
Provider store
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
Provider 就是一个 React 组件,它吸收一个 store 属性,把 store 挂在 React 的 Context 上,如许它的子组件不须要显现的通报 store 就能够猎取到
看个例子
import { Provider } from 'react-redux'
const store = createStore(reducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
那末题目来了,能够猎取到 store 后呢,怎样做交互以及 React 与 Redux 的沟通,这时候 connect API 就派上用场了
照样继承看个例子
import { bindActionCreators } from 'redux'
const App = ({todos, actions}) => (
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
)
const mapStateToProps = state => ({
todos: state.todos
})
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(TodoActions, dispatch)
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
connect 的源码实行大概是如许
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
return function wrapWithConnect(WrappedComponent) {
class Connect extends Component {
constructor(props, context) {
this.store = props.store || context.store
}
render() {
const mappedState = mapStateToProps(store.getState(), this.props)
const mappedDispatch = mapDispatchToProps(store.dispatch, this.props)
const mergedProps = {
mappedState,
mappedDispatch
}
this.renderedElement = createElement(WrappedComponent,mergedProps)
return this.renderedElement
}
}
}
}
这里做了恰当的简化,从这能够看出 connect 返回了一个 Connect 组件猎取到 store,然后把 store.getState() 与 store.dispatch
通报给我们的 mapStateToProps 与 mapDispatchToProps 函数,返回响应的数据与要领经由过程 props 通报给 React 组件,如许 React 组件就能够猎取到响应数据展现,同时也能够经由过程 dispatch 触发 Redux store 的数据更改,Connect 组件在依据数据对照看是不是须要从新衬着~
connect 现实的代码比这庞杂的多,内部做了仔细的浅数据对照以提拔机能
关于 react-redux 这里另有一个潜规则,那就是展现组件与容器组件相星散,就是说只要容器组件处置惩罚数据与状况与 Redux 沟通,
展现组件只做一般的 UI 衬着,能够从这里相识更多 http://redux.js.org/docs/basi…
再看下上面的
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(TodoActions, dispatch)
})
会把传入的函数或对象的每一个要领做下面的变形
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
如许 React 组件挪用对应的 action 时就能够 dispatch 这个 actionCreator 发生的数据
终究不论有无邃晓都能够看下 https://github.com/reactjs/re…
这个例子来加深下明白,以及目次构造的分工,固然有兴致多相识一些例子就更好了
呼,这篇到这里终究算是写完了,末了人人都加油吧!