摘要:中间件对异步的实现非常重要,因为在之前的文章中我们谈到,是一个行为抽象,只是一个对象,是一个纯函数,不应该有调用和副作用的操作。这个函数并不需要保持纯净,它还可以带有副作用,包括执行异步请求。那么如何在中进行网络请求标准的做法是使用。
在之前的浅谈Flux架构及Redux实践一文中我们初步的谈及了Redux的数据流思想,并做了一个简单的加减器。但是还没有接触到Redux更多常用的场景,异步操作、API调用,如何连接到UI层等,Redux可以与很多框架搭配包括Vue、React甚至是纯JavaScript。后面我们会用一个实例--通过github API获取个人信息,来将Redux middleware、async action、连接到React贯穿其中。先看看我们最后写的demo的样子。
Middleware与异步Action依然先看看Redux作者Dam的描述:
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:
action -> middleware modify action -> reducer
它提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。 你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。
在上一篇文章中我们使用的同步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 这个专门的库才能使用。
搭建工作流我们将采用ES6语法,webpack进行打包,webpack-dev-server启一个本地服务器,然后用HMR技术进行React热加载,看看webpack配置信息:
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来进行热更新。
再看看我们最后的目录结构:
├── 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。
Demo Reduxstate
使用Redux非常重要的一点就是设计好顶层的state,在demo中我们需要的state大概长这个样子:
{ 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我们通过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 (); } } export default connect( mapStateToProps, mapDispatchToProps )(App);{"name" in profile ? : ""}
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(Run, 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参考文章
http://cn.redux.js.org/docs/r...
http://www.jianshu.com/p/ab9e...
原文出处 https://github.com/Jiavan/jia... 觉得对你有帮助就给个star吧
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80220.html
摘要:异步实现设计需要增加三种通知异步请求发起的异步请求成功的异步请求失败的示例代码如下返回参数完全可以自定义。这种分别在请求开始前,请求成功后,请求失败后发送。表示数据的有效性,他的作用是在异步请求发送失败后,告诉当前的数据是过时的数据。 说明:对Redux不了解的同学可先看看这篇文章Redux技术架构简介(一) 前言 这里说的Redux异步实现,是专指Redux中的异步Action实现,...
摘要:举例来说一个异步的请求场景,可以如下实现任何异步的逻辑都可以,如等等也可以使用的和。实际上在中,一个就是一个函数。 书籍完整目录 3.4 redux 异步 showImg(https://segmentfault.com/img/bVyou8); 在大多数的前端业务场景中,需要和后端产生异步交互,在本节中,将详细讲解 redux 中的异步方案以及一些异步第三方组件,内容有: redu...
摘要:基本流程创建带有三个方法发出处理数据每次后的数据即得到的数据即一个新的所以是一个对象每次都是上次返回的值所以用即返回的新状态新的即本次的返回值所以每次都是往空的对象里先推再新增属性或改变原来属性的值层通过方法设置监听函数一旦发生变化就会 基本流程 1.创建store,带有三个方法:store.dispatch,store.subscribe,store.getState import ...
摘要:在函数式编程中,异步操作修改全局变量等与函数外部环境发生的交互叫做副作用通常认为这些操作是邪恶肮脏的,并且也是导致的源头。 注:这篇是17年1月的文章,搬运自本人 blog... https://github.com/BuptStEve/... 零、前言 在上一篇中介绍了 Redux 的各项基础 api。接着一步一步地介绍如何与 React 进行结合,并从引入过程中遇到的各个痛点引出 ...
摘要:是官方文档中用到的异步组件,实质就是一个中间件,简单来说就是一个封装表达式的函数,封装的目的是延迟执行表达式。这时我们需要对一般异步中间件进行处理。 曾经前端的革新是以Ajax的出现为分水岭,现代应用中绝大部分页面渲染会以异步流的方式进行。在Redux中,如果要发起异步请求,最合适的位置是在action creator中实现。但我们之前了解到的action都是同步情况,因此需要引入中间...
阅读 3287·2021-11-24 09:39
阅读 3869·2021-11-22 09:34
阅读 4804·2021-08-11 11:17
阅读 1063·2019-08-29 13:58
阅读 2574·2019-08-28 18:18
阅读 538·2019-08-26 12:24
阅读 826·2019-08-26 12:14
阅读 732·2019-08-26 11:58