在以前的浅谈Flux架构及Redux实践一文中咱们初步的谈及了Redux的数据流思想,并作了一个简单的加减器。可是尚未接触到Redux更多经常使用的场景,异步操做、API调用,如何链接到UI层等,Redux能够与不少框架搭配包括Vue、React甚至是纯JavaScript。后面咱们会用一个实例--经过github API获取我的信息,来将Redux middleware、async action、链接到React贯穿其中。先看看咱们最后写的demo的样子。javascript
依然先看看Redux做者Dam的描述:css
It provides a third-party extension point between dispatching an
action, and the moment it reaches the reducer.
个人理解是,middleware提供了一个你能够修改action的机制,这和Express/Koa的中间件有些相似,只不过这里的中间件主要是操做action。中间件对异步的action实现很是重要,由于在以前的文章中咱们谈到,action是一个行为抽象,只是一个对象,reducer是一个纯函数,不该该有API调用和反作用的操做。那么怎么解决异步的问题?咱们确定不能在reducer中写,那么就考虑到了action -> reducer这个过程,这就是redux middleware:html
action -> middleware modify action -> reducer
它提供的是位于 action 被发起以后,到达 reducer 以前的扩展点。 你能够利用 Redux middleware 来进行日志记录、建立崩溃报告、调用异步接口或者路由等等。java
在上一篇文章中咱们使用的同步action,action creator返回的是一个对象,可是异步action能够是一个函数,虽然函数也是对象,这里咱们只是为了区分两种不一样的状况。经过使用指定的 middleware,action creator能够返回函数。这时,这个 action creator 就成为了 thunk。当 action creator 返回函数时,这个函数会被 Redux Thunk middleware 执行。这个函数并不须要保持纯净,它还能够带有反作用,包括执行异步 API 请求。这个函数还能够 dispatch action,就像 dispatch 前面定义的同步 action 同样。那么如何在action中进行网络请求?标准的作法是使用 Redux Thunk middleware。要引入 redux-thunk
这个专门的库才能使用。node
咱们将采用ES6语法,webpack进行打包,webpack-dev-server启一个本地服务器,而后用HMR技术进行React热加载,看看webpack配置信息:react
var webpack = require('webpack'); var OpenBrowserPlugin = require('open-browser-webpack-plugin'); module.exports = { entry: { index: [ 'webpack/hot/dev-server', 'webpack-dev-server/client?http://localhost:8080', './src/index.js', ] }, output: { path: './build', filename: '[name].js', }, devtool: 'source-map', module: { loaders: [{ test: /\.js$/, loader: 'babel', query: { presets: ['es2015', 'stage-0', 'react'], }, }, { test: /\.less$/, loader: 'style!css!less', }], }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), new OpenBrowserPlugin({ url: 'http://localhost:8080' }), ] };
其中open-browser-webpack-plugin
插件将会帮助咱们自动打开浏览器,用babel
进行es编译,less
来维护咱们的css样式,以及使用dev-tool来生成source map,HotModuleReplacementPlugin
来进行热更新。webpack
再看看咱们最后的目录结构:git
├── build │ ├── index.html │ └── index.js ├── node_modules ├── package.json ├── src │ ├── actions │ │ └── actions.js │ ├── components │ │ ├── index.js │ │ ├── Profile │ │ │ ├── Profile.js │ │ │ └── Profile.less │ │ └── Search │ │ ├── Search.js │ │ └── Search.less │ ├── containers │ │ ├── App.js │ │ ├── App.less │ │ └── test.less │ ├── index.html │ ├── index.js │ └── reducers │ └── reducers.js └── webpack.config.js
其中containers
放置咱们的容器组件,components
放置展现性组件,打包入口是index.js
。github
state
使用Redux很是重要的一点就是设计好顶层的state,在demo中咱们须要的state大概长这个样子:web
{ isFetchingData, // boolean username, // string profile, // object }
其中isFetchingData
是网络请求的状态,正在拉取数据为true,username
是咱们要获取用户信息的名字,profile
是咱们拉取用户的详细信息,这个将会是一个Ajax请求,最后由github API提供。
actions
同步action咱们再也不讲述,上一篇文章已经说得比较清楚,这里咱们重点说异步action,app的全部action以下:
export const GET_INFO = 'GET_INFO'; // 获取用户信息 export const FETCHING_DATA = 'FETCHING_DATA'; // 拉取状态 export const RECEIVE_USER_DATA = 'RECEIVE_USER_DATA'; //接收到拉取的状态 // async action creator export function fetchUserInfo(username) { return function (dispatch) { dispatch(fetchingData(true)); return fetch(`https://api.github.com/users/${username}`) .then(response => { console.log(response); return response.json(); }) .then(json => { console.log(json); return json; }) .then((json) => { dispatch(receiveUserData(json)) }) .then(() => dispatch(fetchingData(false))); }; }
上面网络请求用到了fetch
这个API,它会返回一个Promise,还比较新可使用社区提供的polyfill或者使用纯粹的XHR都行,这都不是重点。咱们看看这个action生成函数返回了一个函数,而且在这个函数中还有dispatch
操做,咱们经过中间件传入的dispatch能够用来dispatch actions。在上面的promise链式中首先咱们打印了github API返回Response object,而后输出了json格式的数据,而后dispatch了RECEIVE_USER_DATA
这个action表示接收到了网络请求,并须要修改state(注:这里咱们没有考虑网络请求失败的状况),最后咱们dispatch了FETCHING_DATA
并告诉对应reducer下一个state的isFetchingData为false,表示数据拉取完毕。
reducer
这里看看最核心的reducer,操做profile这一块的:
function profile(state = {}, action) { switch (action.type) { case GET_INFO: return Object.assign({}, state, { username: action.username, }); case RECEIVE_USER_DATA: return Object.assign({}, state, action.profile); default: return state; } } function isFetchingData() {...} function username() {...} const rootReducer = combineReducers({ isFetchingData, username, profile, }); export default rootReducer;
将拉取到的profile对象assign到以前的state,最后经过combineReducers
函数合并为一个reducer。
咱们经过react-redux
提供的connect
方法与Provider
来链接到React,Provider
主要的做用是包装咱们的容器组件,connect
用于将redux与react进行链接,connect() 容许你从 Redux store 中指定准确的 state 到你想要获取的组件中。这让你能获取到任何级别颗粒度的数据,了解更多能够参考它的API,这里咱们再也不敖述。它的形式能够是这样:
function mapStateToProps(state) { return { profile: state.profile, isFetchingData: state.isFetchingData, }; } function mapDispatchToProps(dispatch) { return { fetchUserInfo: (username) => dispatch(fetchUserInfo(username)) }; } class App extends Component { render() { const { fetchUserInfo, profile, isFetchingData } = this.props; return ( <div className='container'> <Search fetchUserInfo={fetchUserInfo} isFetchingData={isFetchingData} /> {'name' in profile ? <Profile profile={profile} isFetchingData={isFetchingData} /> : ''} </div> ); } } export default connect( mapStateToProps, mapDispatchToProps )(App);
connect
是个能够执行两次的柯里化函数,第一次传入的参数至关于一系列的定制化东西,第二次传入的是你要链接的React组件,而后返回一个新的React组件。第一次执行时传入的参数是mapStateToProps, mapDispatchToProps, mergeProps, options。也就是说这里至关于帮组容器选择它在整个Store中所需的state与dispatch回调,这些将会被connect以Props的形式绑定到App容器,咱们能够经过React开发者工具看到这一点:
第一次执行,选择须要的state,第二次传入App容器组件而后返回新的组件。而后建立整个应用的store:
const loggerMiddleware = createLogger(); const store = createStore( rootReducer, compose( applyMiddleware( thunkMiddleware, loggerMiddleware, ), window.devToolsExtension ? window.devToolsExtension() : f => f ) );
这里咱们用到了两个中间件,loggerMiddleware
用于输出咱们每一次的action,能够明确的看到每次action后state的先后状态,thunkMiddleware
用于网络请求处理,最后window.devToolsExtension ? window.devToolsExtension() : f => f
是为了链接咱们的redux-dev-tool,能够明确的看到咱们dispatch的action,还能达到时间旅行的效果。最后经过Provider
输入咱们的store,整个应用就跑起来啦!
let mountRoot = document.getElementById('app'); ReactDOM.render( <Provider store={store}> <App /> </Provider>, mountRoot );
命令行输入npm run dev
,整个应用就跑起来了,在输入框输入Jiavan,咱们来看看action与数据流:
在console面板,logger中间件为咱们打印除了每一次dispatch action以及先后的state值,是否是很是直观,然而厉害的还在后面。redux-dev-tool能够直接查看咱们state tree以及对action作undo操做达到时间旅行的效果!
完整的demo在文章最后将会贴出,如今总结下:首先咱们规划了整个应用的state,而后进行数据流层的代码开发,同步异步action的编写以及reducer的开发,再经过选择咱们容器组件所需的state与dispatch回调经过connect方法绑定后输出新的组件,经过建立store与Provider将其与React链接,这样整个应用的任督二脉就被打通了。最后极力推荐Redux的官方文档。
完整demo -> https://github.com/Jiavan/rea...
运行
1. npm install 2. webpack 3. npm run dev
原文出处 https://github.com/Jiavan/jia... 以为对你有帮助就给个star吧