react调用api等待返回结果_Redux + React-Router 的入门教学和配置教程,看这一篇就够了...-深圳网站优化推广公司

react调用api等待返回结果_Redux + React-Router 的入门教学和配置教程,看这一篇就够了...

前言

React 是单向数据流,数据通过 props 从父节点传递到子节点。如果顶层的某个 props 改变了, React 会重新渲染所有的子节点。注意:props 是只读的(即不可以使用 this.props 直接修改 props),它是用于在整个组件树中传递数据和配置。

每个组件都有属于自己的 statestateprops 的区别在于 state 只存在于组件内部。注意 ⚠️:只能从当前组件调用 this.setState 方法修改 state 值(不可以直接修改 this.state)。

可见,更新子组件有两种方式,一种是改变子组件自身的 state 值,另一种则是更新子组件从父组件接收到的 this.props 值从而达到更新。

React 项目开发过程中,我们大多时候需要让组件共享某些数据。一般来说,我们可以通过在组件间传递数据(通过 props )的方式实现数据共享,然而,当数据需要在非父子关系的组件间传递时操作起来则变得十分麻烦,而且容易让代码的可读性降低,这时候我们就需要使用 state(状态)管理工具。

常见的状态管理工具有 ReduxMobx。由于 Redux 提供了状态管理的整个架构,并有着清晰的约束规则,适合在大型多人开发的应用中使用。本文介绍的是如何在 React 项目中使用 Redux 进行状态管理。

进入正题

本节主要介绍 ReduxReact-Router 相关的基础知识和相关配置教程。

Redux - 基本概念

Redux 适用于多交互、多数据源的场景。从组件角度看,如果我们的应用有以下场景,则可以考虑在项目中使用 Redux

某个组件的状态,需要共享某个状态需要在任何地方都可以拿到一个组件需要改变全局状态一个组件需要改变另一个组件的状态

当我们的应用符合以上提到的场景时,若不使用 Redux 或者其他状态管理工具,不按照一定规律处理状态的读写,项目代码的可读性将大大降低,不利于团队开发效率的提升。

1bf4306f18ef7fd66edeecfedaa97cf0.png

如上图所示,Redux 通过将所有的 state 集中到组件顶部,能够灵活的将所有 state 各取所需地分发给所有的组件。

Redux 的三大原则:

整个应用的 state 都被存储在一棵 object tree 中,并且 object tree 只存在于唯一的 store 中(这并不意味使用 Redux 就需要将所有的 state 存到 redux 上,组件还是可以维护自身的 state )。 state 是只读的。 state 的变化,会导致视图( view)的变化。用户接触不到 state,只能接触到视图,唯一改变 state 的方式则是在视图中触发 actionaction 是一个用于描述已发生事件的普通对象。使用 reducers 来执行 state 的更新。 reducers 是一个纯函数,它接受 action 和当前 state 作为参数,通过计算返回一个新的 state ,从而实现视图的更新。
a7dbfeaf600d4ce729ccda9924b78100.png

如上图所示,redux 的工作流程大致如下:

首先,用户在视图中通过 store.dispatch 方法发出 action

然后,store 自动调用 reducers,并且传入两个参数:当前 state 和收到的 actionreducers 会返回新的 state

最后,当 store 监听到 state 的变化,就会调用监听函数,触发视图的重新渲染。

放一张图加深理解:

1d6cb1bd7cb04412dd47db3053ab58a7.gif

下面我们来介绍一下 Redux 的 API 相关内容。

Store

Store 就是保存数据的地方,整个应用只能有一个 Store

Redux 提供 createStore 这个函数,用来创建一个 Store 以存放整个应用的 state

import { createStore } from 'redux';const store = createStore(reducer, [preloadedState], enhancer);

可以看到,createStore 接受 reducer、初始 state(可选)和增强器作为参数,返回一个新的 store 对象。

State

store 对象包含所有数据。如果想得到某个时点的数据,就要对 store 生成快照。这种时点的数据集合,就叫做 state

如果要获取当前时刻的 state,可以通过 store.getState() 方法拿到:

import { createStore } from 'redux';const store = createStore(reducer, [preloadedState], enhancer);const state = store.getState();

Action

state 的变化,会导致视图的变化。但是,用户接触不到 state,只能接触到视图。所以,state 的变化必须是由视图发起的。

action 就是视图发出的通知,通知 store 此时的 state 应该要发生变化了。

action 是一个对象。其中的 type 属性是必须的,表示 action 的名称。其他属性可以自由设置,社区有一个规范可以参考:

const action = { type: 'ADD_TODO', payload: 'Learn Redux' // 可选属性};

上面代码定义了一个名称为 ADD_TODOaction,它携带的数据信息是 Learn Redux

Action Creator

view 要发送多少种消息,就会有多少种 action,如果都手写,会很麻烦。

可以定义一个函数来生成 action,这个函数就称作 Action Creator,如下面代码中的 addTodo 函数:

const ADD_TODO = '添加 TODO';function addTodo(text) { return { type: ADD_TODO, text }}const action = addTodo('Learn Redux');

redux-actions 是一个实用的库,让编写 redux 状态管理变得简单起来。该库提供了 createAction 方法用于创建动作创建器:

import { createAction } from "redux-actions"export const INCREMENT = 'INCREMENT'export const increment = createAction(INCREMENT)

上边代码定义一个动作 INCREMENT, 然后通过 createAction 创建了对应 Action Creator: 调用 increment() 时就会返回 { type: 'INCREMENT' } 调用 increment(10) 返回 { type: 'INCREMENT', payload: 10 }

store.dispatch()

store.dispatch() 是视图发出 action 的唯一方法,该方法接受一个 action 对象作为参数:

import { createStore } from 'redux';const store = createStore(reducer, [preloadedState], enhancer);store.dispatch({ type: 'ADD_TODO', payload: 'Learn Redux'});

结合 Action Creator,这段代码可以改写如下:

import { createStore } from 'redux';import { createAction } from "redux-actions"const store = createStore(reducer, [preloadedState], enhancer);const ADD_TODO = 'ADD_TODO';const add_todo = createAction('ADD_TODO'); // 创建 Action Creatorstore.dispatch(add_todo('Learn Redux'));

reducer

store 收到 action 以后,必须给出一个新的 state,这样视图才会进行更新。state 的计算(更新)过程则是通过 reducer 实现。

reducer 是一个函数,它接受 action 和当前 state 作为参数,返回一个新的 state

const reducer = function (state, action) { // ... return new_state;};

为了实现调用 store.dispatch 方法时自动执行 reducer 函数,需要在创建 store 时将将 reducer 传入 createStore 方法:

import { createStore } from 'redux';const reducer = function (state, action) { // ... return new_state;};const store = createStore(reducer);

上面代码中,createStore 方法接受 reducer 作为参数,生成一个新的 store。以后每当视图使用 store.dispatch 发送给 store 一个新的 action,就会自动调用 reducer 函数,得到更新的 state

redux-actions 提供了 handleActions 方法用于处理多个 action

// 使用方法:// handleActions(reducerMap, defaultState)import { handleActions } from 'redux-actions';const initialState = {  counter: 0 };const reducer = handleActions( { INCREMENT: (state, action) => ({ counter: state.counter + action.payload }), DECREMENT: (state, action) => ({ counter: state.counter - action.payload }) }, initialState,);

拆分、合并 reducer

前面提到,在一个 React 应用中只能有一个 store 用于存放应用的 state。组件通过调用 action 函数,传递数据到 reducerreducer 根据数据更新对应的 state

对于大型应用来说,应用的 state 必然十分庞大,导致 reducer 的复杂度也随着变大。

关于拆分

在这个时候,就可以考虑将 reducer 拆分成多个单独的函数,让每个函数负责独立管理 state 的一部分。

关于合并

redux 提供了 combineReducers 辅助函数,可将独立分散的 reducer 合并成一个最终的 reducer 函数,然后在创建 store 时作为 createStore 的参数传入。

我们可以根据业务需要,把所有子 reducer 放在不同的目录下,然后在在一个文件里面统一引入,最后将合并后的 reducer 导出:

// src/model/reducers.tsimport { combineReducers } from 'redux';import UI from './UI/reducers';import user from './user/reducers';import content from './content/reducers';const rootReducer = combineReducers({ UI, user, content,});export default rootReducer;

中间件及异步操作

redux 而言,同步指的是当视图发出 action 以后,reducer 立即算出 state(原始的 redux 工作流程),而异步指的是在 action 发出以后,过一段时间再执行 reducer

同步通常发生在原生 redux 的工作流程中,而在大多数实际场景中,更多的是需要异步操作:action 发出以后,在进入 reducer 之前需要先完成一个异步任务,比如发送 ajax 请求后拿到数据后,再进入 reducer 执行计算并对 state 进行更新。

显然原生的 redux 是不支持异步操作的,这就要用到新的工具——中间件(middleware)来处理这种业务场景。从本质上来讲中间件是对 store.dispatch 方法进行了拓展。

中间件提供位于 action 发起之后,到达 reducer 之前的扩展点:即通过 store.dispatch 方法发出的 action 会依次经过各个中间件,最终到达 reducer

d06dc33a3c901d8d9a616719c279ae21.png

我们可以利用中间件来进行日志记录(redux-logger)、创建崩溃报告(自己写 crashReporter)、调用异步接口(redux-saga)或者路由(connected-react-router)等操作。

redux 提供了一个原生的 applyMiddleware 方法,它的作用是将所有中间件组成一个数组,依次执行。假如要使用 redux-logger 来实现日志记录功能,用法如下:

import { applyMiddleware, createStore } from 'redux';import createLogger from 'redux-logger';const logger = createLogger();const store = createStore( reducer, applyMiddleware(logger));

如果有多个中间件,则将中间件依次作为参数传入 applyMiddleware 方法中:

import { applyMiddleware, createStore } from 'redux';import createLogger from 'redux-logger';import createSagaMiddleware from 'redux-saga';const logger = createLogger(); // 日志记录const sagaMiddleware = createSagaMiddleware(); // 调用异步接口let middleware = [sagaMiddleware];middleware.push(logger);const store = createStore( reducer, // 可传initial_state applyMiddleware(...middleware));

需要注意的是: createStore 方法可以接受整个应用的初始状态作为参数(可选),若传入初始状态,applyMiddleware 则需要作为第三个参数。 有的中间件有次序要求,使用前要查一下文档(如 redux-logger一定要放在最后,否则输出结果会不正确)。

React-Redux 概念介绍

前面小节介绍的 Redux 本身是一个可以结合 ReactVueAngular 甚至是原生 JavaScript 应用使用的状态库。

为了让 Redux 帮我们管理 React 应用的状态,需要把 ReduxReact 连接,官方提供了 React-Redux 库(这个库是可以选用的,也可以只用 Redux)。

React-Redux 将所有组件分成 UI 组件和容器组件两大类:UI 组件只负责 UI 的呈现,不含有状态(this.state),所有数据都由 this.props 提供,且不使用任何 ReduxAPI。容器组件负责管理数据和业务逻辑,含有状态(this.state),可使用 ReduxAPI

简而言之,容器组件作为 UI 组件的父组件,负责与外部进行通信,将数据通过 props 传给 UI 组件渲染出视图。

React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给 React-Redux

下面我们来介绍一下 React-Redux API 相关的内容

connect 方法

React-Redux 提供了 connect 方法,用于将 UI 组件生成容器组件:

import { connect } from 'react-redux'class Dashboard extends React.Component { ... // 组件内部可以获取 this.props.loading 的值}const mapStateToProps = (state) => { return { loading: state.loading, }}// 将通过 connect 方法自动生成的容器组件导出export default connect( mapStateToProps, // 可选 // mapDispatchToProps, // 可选)(Dashboard)

从上面代码可以看到,connect 方法接受两个可选参数用于定义容器组件的业务逻辑: mapStateToProps 负责输入逻辑,即将 state 映射成传入 UI 组件的参数(props) mapDispatchToProps 负责输出逻辑,即将用户对 UI 组件的操作映射成 action

注意:当 connect 方法不传入任何参数时,生成的容器组件只可以看作是对 UI 组件做了一个单纯的包装,不含有任何的业务逻辑: 省略 mapStateToProps 参数, UI 组件就不会订阅 store,即 store 的更新不会引起 UI 组件的更新。 省略 mapDispatchToProps 参数, UI 组件就不会将用户的操作当作 action 发送数据给 store,需要在组件中手动调用 store.dispatch 方法。

mapStateToProps

mapStateToProps 是一个函数,它的作用就是建立一个从 state对象(外部)到 UI 组件 props对象的映射关系。该函数会订阅 整个应用的 store,每当 state 更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。

mapStateToProps 的第一个参数总是 state 对象,还可以使用第二个参数(可选),代表容器组件的 props 对象:

// 容器组件的代码// // All// const mapStateToProps = (state, ownProps) => { return { active: ownProps.showType === "SHOW_ALL", loading: state.loading, }}

使用 ownProps 作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。

mapDispatchToProps

mapDispatchToPropsconnect 函数的第二个参数,用来建立 UI 组件的参数到 store.dispatch 方法的映射。

由于在项目中大多使用 mapDispatchToProps 比较少,这里不进行细讲。关于mapStateToPropsmapDispatchToPropsconnect 的更详细用法说明可以查看文档。

Provider 组件

使用 connect 方法生成容器组件以后,需要让容器组件拿到 state 对象,才能生成 UI 组件 的参数。

react-redux 提供了 Provider 组件,可以让容器组件拿到 state,具体用法是需要用 Provider 组件包裹项目的根组件(如App),使得根组件所有的子组件都可以默认获取到 state 对象:

import * as React from 'react';import * as ReactDOM from 'react-dom';import { Provider } from 'react-redux';import App from './App';import { store } from './store/configureStore';ReactDOM.render( , document.getElementById('root'),);

react-router

react-router 是完整的 react 的路由解决方案,它保持 UIURL 的同步。在项目中我们使用的是最新的 v4 版。

需要注意的是,在开发中不应该直接安装 react-router ,因为:在 v4 版中 react-router 被划分为三个包:react-routerreact-router-domreact-router-native,它们的区别如下:react-router:提供核心的路由组件和函数。react-router-dom:提供浏览器使用的路由组件和函数。react-router-native:提供 react-native 对应平台使用的路由组件和函数。

当我们的 react 应用同时使用了 react-routerredux,则可以将两者进行更深度的整合,实现:将 router 的数据与 store 进行同步,并且可以从 store 访问 router 数据,可使用 this.props.dispatch 方法发送 action。通过 dispatch actions 导航,个人理解是可使用 store.dispatch(push('routerName')) 切换路由。在 redux devtools 中支持路由改变的时间旅行调试。

想要实现以上的目标,则可以通过 connected-react-routerhistory 两个库进行实现,步骤如下:在创建 store 的文件添加配置,包括创建 history 对象、使用 connected-react-router 提供的 connectRouter 方法和 history 对象创建 root reducer、使用 connected-react-router 提供的 routerMiddleware 中间件和 history 对象实现 dispatch actions 导航。

import { connectRouter, routerMiddleware } from 'connected-react-router';import createHistory from 'history/createBrowserHistory';import { createStore, applyMiddleware } from 'redux';import { createLogger } from 'redux-logger';import createSagaMiddleware from 'redux-saga';import reducer from '../model/reducers';export const history = createHistory();const sagaMiddleware = createSagaMiddleware(); // 调用异步接口let middleware = [sagaMiddleware, routerMiddleware(history)];const logger = createLogger(); // 日志记录middleware.push(logger);const initialState = {};const store = createStore( connectRouter(history)(reducer), initialState, applyMiddleware(...middleware));

在项目入口文件 index.js 中为根组件中添加配置,包括使用 connected-react-router 提供的 ConnectedRouter 组件包裹路由,将ConnectedRouter 组件作为Provider的子组,并且将在 store 中创建的 history 对象引入,将其作为 props 属性 传入ConnectedRouter 组件

import * as React from 'react';import * as ReactDOM from 'react-dom';import { Provider } from 'react-redux'import { ConnectedRouter } from 'connected-react-router'import App from './App'import rootSaga from './model/sagas';import { store, history } from './store/configureStore';ReactDOM.render( , document.getElementById('root'),);

以上则完成了 react-router 和 redux的深度整合 ✌️。

总结

  • 本文介绍的是如何在 React 项目中使用 redux 进行状态管理,并对相关基础知识进行介绍和展示了完整的代码。
  • 在进行业务代码开发前通常会对项目进行的一些特殊配置,有利于后期的工程开发,具体内容可参考 :react + typescript 项目的定制化过程。

版权

  • 作者:前端小黑
  • 链接:https://juejin.im/post/5dcaaa276fb9a04a965e2c9b
  • 著作权归作者所有。
weixin_39830012 CSDN认证博客专家 CSDN认证企业博客
码龄7年 暂无认证
163
原创
-
周排名
218万+
总排名
39万+
访问
等级
392
积分
46
粉丝
39
获赞
0
评论
472
收藏
私信
为什么被折叠? 到【灌水乐园】发言
前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

相关内容推荐

韶关网站设计网站设计功能网站界面设计要求在线家居设计网站设计公司网站设计装修 设计 网站网站公司网站设计普洱网站设计如何把DZ制作成文库网站贵阳定制网站制作著名网站设计学生网站设计包装设计好的网站好的平面设计网站推荐网站单页设计网上设计网站面包搜索网站制作设计师互动网站怎么制作一个网站卖东西淮北网站设计温州网站制作壁纸的软件大宁网站制作网站前端设计师招聘设计网站制作表格衡水响应式网站制作网页制作与网站建设基础青涩网站制作干花网站设计加油站制作招聘网站app国内外知名设计网站基于php技术的网站设计赣州网站设计禅城区电商网站制作68设计网站新加坡网站制作奶茶改图网站制作特色网站设计网站的设计与制作任务书gif制作网站知乎北京网站设计公司推广哪家好网站设计分享政府网站页面设计有什么好的平面设计网站网站设计制造巫溪网站设计张掖网站制作和推广设计师网站建设深圳网站制作郑州规划和设计网站平面设计公司网站大型网站数据库设计糕点网站设计家校通网站制作奶茶永康如何制作p2p通信网站官方网站怎样制作网站设计 杭州沭阳网站制作培训app网站设计香港网站设计温州网站制作壁纸的软件模拟网站设计日本 网站 设计网站设计网格全球设计网站排名北京网站设计培训邯郸门户网站制作制作视频去哪些网站赚钱学习广告设计的网站晋州网站建设制作一卡通网站制作美食国内扁平化设计网站网站设计工具有没有教做点心制作的网站合肥公司网站设计合肥网站建设设计高端的汽车行业网站制作顺德网站设计公司设计师兼职的网站产品设计平台网站网站后台如何设计电子商务网站毕业设计皮具网站设计好的景观设计网站设计征稿网站空间设计网站东莞网站制作首荐祥奔科技闲鱼镜像网站制作招聘设计师去什么网站六安网站制作小玩具淘宝常用素材下载和制作网站阜新网站设计字体设计欣赏网站网站的设计与实现论文武穴网站设计制作开发开封网站设计设计传单的网站成都网站设计制作公司如何制作自己的网站公司制作购物网站首页代码塔城网站设计制作app设计网站家居装修设计网站A5网站视频制作室内装饰设计网站logo在线设计网站池州英文网站制作ps设计网站网站制作口碑网站毕业设计开题报告旅游网站的设计网站首页设计图设计电商网站制作一个企业的网站流程设计装饰公司网站深圳网站设计公司培训机构网站设计室内设计网站国外地产广告设计网站网站设计建设公司网站制作大学生自学网如何制作网站链接优化后期制作的动画设计网站搜索网站设计朝阳标书制作网站山东青岛网站设计设计师推荐网站产品设计网站大全大型网站架构设计图无锡网站制作都询新互动网络泉州哪里有制作网站的设计教育网站国外知名工业设计网站店面设计网站重庆网站设计公司制作网页代码网站交友网站规划设计书设计理念网站海外华人网站制作书签怎么使用dw制作网站顶尖设计网站用SPD制作网站主页九设计网站网站的前端设计师设计衣服网站图书馆网站设计河南网站制作定制设计类网站有哪些宁夏网站制作教程网站设计 专业网站设计名称c 网站制作导航栏西瓜网站制作贴纸漫画网站制作冰淇淋大朗定制网站制作网站设计制造优秀的个人网站设计手机网站怎么制作飞科网网页设计和网站设计黄浦网站设计苏州 网站设计购物网站课程设计报告网站设计脚本服装设计入门学习网站网站制作网站设计大有网站设计包豪斯设计学院网站页面设计漂亮的网站申论模板网站制作独立网站设计日本著名设计网站酒色影视网站制作企业网站制作费入什么科目网站微信制作哪家好广州网页网站设计REAL电影网站制作音乐网站设计报告网站设计模板免费网上商城网站设计网站设计与制作基础心得体会微网站设计制作免费自己制作网站方法虚拟主机空间制作网站网站制作的原则和思路别墅设计网站网站设计师文字logo制作网站广安网站建设制作价格哪家便宜新沂市网站制作开发策划设计网站分享服装网站设计报告网站设计奖t恤在线设计网站珠宝设计师网站html制作个人简历网站快速制作banner的网站齐齐哈尔网站制作费用义乌日本网站制作需要多少钱怎么制作表白网站链接网站什么设计十大高端网站定制设计

合作伙伴

深圳网站优化推广公司

龙岗网络公司
深圳网站优化
龙岗网站建设
坪山网站建设
百度标王推广
天下网标王
SEO优化按天计费
SEO按天计费系统