react+redux+router异步数据获取教程
react的FLUX数据流一直搞不清楚,他不像 不过我总算先搞定了 keywords
Basic Usage1st 实现action方法export const addDeck = name => ({ type: 'ADD_DECK',data: name }); 2nd 根据action方法创建reducer方法export const showBack = (state,action) => { switch(action.type) { case 'SHOW_BACK': return action.data || false; default: return state || false; } }; 3rd 根据reducer方法创建storeconst store = createStore(combineReducers(reducers));
let unsubscribe = store.subscribe(() => console.log(store.getState()) ); unsubscribe(); 4th 引入react-redux的<Provider>,导入store<Provider store={store}> {...} </Provider> 5th react组件中通过connect方法绑定store和dispatch。const mapStateToProps = (newTalks) => ({ newTalks }); const mapDispatchToProps = dispatch => ({ testFunc: () => dispatch(updataTalkLists(1)),receiveData: () => dispatch(receiveData()) }); export default connect(mapStateToProps,mapDispatchToProps)(MainPage); 6th this.props中直接调用action方法。this.props.receiveData With react-router结合router使用时需要有2步。 1st 绑定routing到reducer上import { syncHistoryWithStore,routerReducer } from 'react-router-redux'; import * as reducers from './redux/reducer'; reducers.routing = routerReducer; const store = createStore(combineReducers(reducers)); 2nd 使用syncHistoryWithStore绑定store和browserHistoryconst history = syncHistoryWithStore(browserHistory,store); <Provider store={store}> <Router history={history}> {routes} </Router> </Provider> Async类似 Express 或 Koa 框架中的中间件。它提供的是位于 action 被发起之后,到达 reducer 之前的扩展。
实现action异步操作,必须要引入middleware。我这里用了 1st 创建store是引入Middlewareimport thunkMiddleware from 'redux-thunk'; import { createStore,combineReducers,applyMiddleware } from 'redux'; const store = createStore(combineReducers(reducers),applyMiddleware(thunkMiddleware)); 2nd 创建一个可以执行dispacth的action这也是中间件的作用所在。 export const receiveData = data => ({ type: 'RECEIVE_DATA',data: data }); export const fetchData = () => { return dispatch => { fetch('/api/data') .then(res => res.json()) .then(json => dispatch(receiveData(json))); }; }; 3rd 组件中对异步的store元素有相应的判断操作。React组件会在store值发生变化时自动调用render()方法,更新异步数据。但是我们同样也需要处理异步数据没有返回或者请求失败的情况。否则渲染会失败,页面卡住。 if(!data.newTalks) { return(<div/>); } 相关知识Store的实现Store提供了3个方法 import { createStore } from 'redux'; let { subscribe,//监听store变化 dispatch,//调用action方法 getState //返回当前store } = createStore(reducer); 下面是create方法的一个简单实现 const createStore = (reducer) => { let state; let listeners = []; const getState = () => state; const dispatch = (action) => { state = reducer(state,action); listeners.forEach(listener => listener()); }; const subscribe = (listener) => { listeners.push(listener); return () => { listeners = listeners.filter(l => l !== listener); } }; dispatch({}); return { getState,dispatch,subscribe }; }; combineReducer的简单实现const combineReducers = reducers => { return (state = {},action) => { return Object.keys(reducers).reduce( (nextState,key) => { nextState[key] = reducers[key](state[key],action); return nextState; },{} ); }; }; 中间件
const store = createStore( reducer,initial_state,applyMiddleware(thunk,promise,logger) ); applyMiddlewares的实现,它是将所有中间件组成一个数组,依次执行 export default function applyMiddleware(...middlewares) { return (createStore) => (reducer,preloadedState,enhancer) => { var store = createStore(reducer,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} } } 上面代码中,所有中间件被处理后得到一个数组保存在chain中。之后将chain传给compose,并将store.dispatch传给返回的函数。。可以看到,中间件内部(middlewareAPI)可以拿到getState和dispatch这两个方法。 那么在这里面做了什么呢?我们再看compose的实现: export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } else { const last = funcs[funcs.length - 1] const rest = funcs.slice(0,-1) return (...args) => rest.reduceRight((composed,f) => f(composed),last(...args)) } } compose中的核心动作就是将传进来的所有函数倒序(reduceRight)进行如下处理: (composed,f) => f(composed) 我们知道Array.prototype.reduceRight是从右向左累计计算的,会将上一次的计算结果作为本次计算的输入。再看看applyMiddleware中的调用代码: dispatch = compose(...chain)(store.dispatch) compose函数最终返回的函数被作为了dispatch函数,结合官方文档和代码,不难得出,中间件的定义形式为: function middleware({dispatch,getState}) { return function (next) { return function (action) { return next(action); } } } 或 middleware = (dispatch,getState) => next => action => { next(action); } 也就是说,redux的中间件是一个函数,该函数接收dispatch和getState作为参数,返回一个以dispatch为参数的函数,这个函数的返回值是接收action为参数的函数(可以看做另一个dispatch函数)。在中间件链中,以dispatch为参数的函数的返回值将作为下一个中间件(准确的说应该是返回值)的参数,下一个中间件将它的返回值接着往下一个中间件传递,最终实现了store.dispatch在中间件间的传递。 redux-promise中间件既然 Action Creator 可以返回函数,当然也可以返回其他值。另一种异步操作的解决方案,就是让 Action Creator 返回一个 Promise 对象。
const fetchPosts = (dispatch,postTitle) => new Promise(function (resolve,reject) { dispatch(requestPosts(postTitle)); return fetch(`/some/API/${postTitle}.json`) .then(response => { type: 'FETCH_POSTS',payload: response.json() }); });
import { createAction } from 'redux-actions'; class AsyncApp extends Component { componentDidMount() { const { dispatch,selectedPost } = this.props // 发出同步 Action dispatch(requestPosts(selectedPost)); // 发出异步 Action dispatch(createAction( 'FETCH_POSTS',fetch(`/some/API/${postTitle}.json`) .then(response => response.json()) )); } 上面代码中,第二个dispatch方法发出的是异步 Action,只有等到操作结束,这个 Action 才会实际发出。注意,createAction的第二个参数必须是一个 Promise 对象。 redux-promise的源码 export default function promiseMiddleware({ dispatch }) { return next => action => { if (!isFSA(action)) { return isPromise(action) ? action.then(dispatch) : next(action); } return isPromise(action.payload) ? action.payload.then( result => dispatch({ ...action,payload: result }),error => { dispatch({ ...action,payload: error,error: true }); return Promise.reject(error); } ) : next(action); }; } 从上面代码可以看出,如果 Action 本身是一个 Promise,它 resolve 以后的值应该是一个 Action 对象,会被dispatch方法送出(action.then(dispatch)),但 reject 以后不会有任何动作;如果 Action 对象的payload属性是一个 Promise 对象,那么无论 resolve 和 reject,dispatch方法都会发出 Action。 mapStateToProps()
// 容器组件的代码 // <FilterLink filter="SHOW_ALL"> // All // </FilterLink> const mapStateToProps = (state,ownProps) => { return { active: ownProps.filter === state.visibilityFilter } } mapDispatchToProps()mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
const mapDispatchToProps = ( dispatch,ownProps ) => { return { onClick: () => { dispatch({ type: 'SET_VISIBILITY_FILTER',filter: ownProps.filter }); } }; } 如果 const mapDispatchToProps = { onClick: (filter) => { type: 'SET_VISIBILITY_FILTER',filter: filter }; }
|