摘要:编辑器顶层组件不就了吗这就是。官方提供的绑定库。具有高效且灵活的特性。在的中,可以使用或者等来监听某个,当某个触发后,可以使用发起异步操作,操作完成后使用函数触发,同步更新,从而完成整个的更新。
不就ok了吗?这就是 react-redux。Redux 官方提供的 React 绑定库。 具有高效且灵活的特性。
React Redux 将组件区分为 容器组件 和 UI 组件
前者会处理逻辑
后者只负责显示和交互,内部不处理逻辑,状态完全由外部掌控
两个核心
Provider
看我上边那个代码的顶层组件4个字。对,你没有猜错。这个顶级组件就是Provider,一般我们都将顶层组件包裹在Provider组件之中,这样的话,所有组件就都可以在react-redux的控制之下了,但是store必须作为参数放到Provider组件中去
这个组件的目的是让所有组件都能够访问到Redux中的数据。
connect
这个才是react-redux中比较难的部分,我们详细解释一下
首先,先记住下边的这行代码:
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
这个单词翻译过来就是把state映射到props中去 ,其实也就是把Redux中的数据映射到React中的props中去。
举个栗子:
const mapStateToProps = (state) => { return { // prop : state.xxx | 意思是将state中的某个数据映射到props中 foo: state.bar } }
然后渲染的时候就可以使用this.props.foo
class Foo extends Component { constructor(props){ super(props); } render(){ return( // 这样子渲染的其实就是state.bar的数据了
this.props.foo) } } Foo = connect()(Foo); export default Foo;
然后这样就可以完成渲染了
这个单词翻译过来就是就是把各种dispatch也变成了props让你可以直接使用
const mapDispatchToProps = (dispatch) => { // 默认传递参数就是dispatch return { onClick: () => { dispatch({ type: "increatment" }); } }; }
class Foo extends Component { constructor(props){ super(props); } render(){ return( ) } } Foo = connect()(Foo); export default Foo;
组件也就改成了上边这样,可以直接通过this.props.onClick,来调用dispatch,这样子就不需要在代码中来进行store.dispatch了
react-redux的基本介绍就到这里了
redux-saga
如果按照原始的redux工作流程,当组件中产生一个action后会直接触发reducer修改state,reducer又是一个纯函数,也就是不能再reducer中进行异步操作;
而往往实际中,组件中发生的action后,在进入reducer之前需要完成一个异步任务,比如发送ajax请求后拿到数据后,再进入reducer,显然原生的redux是不支持这种操作的
这个时候急需一个中间件来处理这种业务场景,目前最优雅的处理方式自然就是redux-saga
核心讲解
1、Saga 辅助函数
redux-saga提供了一些辅助函数,用来在一些特定的action 被发起到Store时派生任务,下面我先来讲解两个辅助函数:takeEvery 和 takeLatest
takeEvery就像一个流水线的洗碗工,过来一个脏盘子就直接执行后面的洗碗函数,一旦你请了这个洗碗工他会一直执行这个工作,绝对不会停止接盘子的监听过程和触发洗盘子函数
例如:每次点击 按钮去Fetch获取数据时时,我们发起一个 FETCH_REQUESTED 的 action。 我们想通过启动一个任务从服务器获取一些数据,来处理这个action,类似于
window.addEventLister("xxx",fn)
当dispatch xxx的时候,就会执行fn方法,
首先我们创建一个将执行异步 action 的任务(也就是上边的fn):
// put:你就认为put就等于 dispatch就可以了; // call:可以理解为实行一个异步函数,是阻塞型的,只有运行完后面的函数,才会继续往下; // 在这里可以片面的理解为async中的await!但写法直观多了! import { call, put } from "redux-saga/effects" export function* fetchData(action) { try { const apiAjax = (params) => fetch(url, params); const data = yield call(apiAjax); yield put({type: "FETCH_SUCCEEDED", data}); } catch (error) { yield put({type: "FETCH_FAILED", error}); } }
然后在每次 FETCH_REQUESTED action 被发起时启动上面的任务,也就相当于每次触发一个名字为 FETCH_REQUESTED 的action就会执行上边的任务,代码如下
import { takeEvery } from "redux-saga" function* watchFetchData() { yield* takeEvery("FETCH_REQUESTED", fetchData) }
注意:上面的 takeEvery 函数可以使用下面的写法替换
function* watchFetchData() { while(true){ yield take("FETCH_REQUESTED"); yield fork(fetchData); } }
在上面的例子中,takeEvery 允许多个 fetchData 实例同时启动,在某个特定时刻,我们可以启动一个新的 fetchData 任务, 尽管之前还有一个或多个 fetchData 尚未结束
如果我们只想得到最新那个请求的响应(例如,始终显示最新版本的数据),我们可以使用 takeLatest 辅助函数
import { takeLatest } from "redux-saga" function* watchFetchData() { yield* takeLatest("FETCH_REQUESTED", fetchData) }
和takeEvery不同,在任何时刻 takeLatest 只允许执行一个 fetchData 任务,并且这个任务是最后被启动的那个,如果之前已经有一个任务在执行,那之前的这个任务会自动被取消
2、Effect Creators
redux-saga框架提供了很多创建effect的函数,下面我们就来简单的介绍下开发中最常用的几种
take(pattern)
put(action)
call(fn, ...args)
fork(fn, ...args)
select(selector, ...args)
take函数可以理解为监听未来的action,它创建了一个命令对象,告诉middleware等待一个特定的action, Generator会暂停,直到一个与pattern匹配的action被发起,才会继续执行下面的语句,也就是说,take是一个阻塞的 effect
用法:
function* watchFetchData() { while(true) { // 监听一个type为 "FETCH_REQUESTED" 的action的执行,直到等到这个Action被触发,才会接着执行下面的 yield fork(fetchData) 语句 yield take("FETCH_REQUESTED"); yield fork(fetchData); } }
put函数是用来发送action的 effect,你可以简单的把它理解成为redux框架中的dispatch函数,当put一个action后,reducer中就会计算新的state并返回,注意: put 也是阻塞 effect
用法:
export function* toggleItemFlow() { let list = [] // 发送一个type为 "UPDATE_DATA" 的Action,用来更新数据,参数为 `data:list` yield put({ type: actionTypes.UPDATE_DATA, data: list }) }
call函数你可以把它简单的理解为就是可以调用其他函数的函数,它命令 middleware 来调用fn 函数, args为函数的参数,注意: fn 函数可以是一个 Generator 函数,也可以是一个返回 Promise 的普通函数,call 函数也是阻塞 effect
用法:
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) export function* removeItem() { try { // 这里call 函数就调用了 delay 函数,delay 函数为一个返回promise 的函数 return yield call(delay, 500) } catch (err) { yield put({type: actionTypes.ERROR}) } }
fork 函数和 call 函数很像,都是用来调用其他函数的,但是fork函数是非阻塞函数,也就是说,程序执行完 yield fork(fn, args) 这一行代码后,会立即接着执行下一行代码语句,而不会等待fn函数返回结果后,在执行下面的语句
用法:
import { fork } from "redux-saga/effects" export default function* rootSaga() { // 下面的四个 Generator 函数会一次执行,不会阻塞执行 yield fork(addItemFlow) yield fork(removeItemFlow) yield fork(toggleItemFlow) yield fork(modifyItem) }
select 函数是用来指示 middleware调用提供的选择器获取Store上的state数据,你也可以简单的把它理解为redux框架中获取store上的 state数据一样的功能 :store.getState()
用法:
export function* toggleItemFlow() { // 通过 select effect 来获取 全局 state上的 `getTodoList` 中的 list let tempList = yield select(state => state.getTodoList.list) }
一个具体的实例
**index.js **
import React from "react"; import ReactDOM from "react-dom"; import {createStore, applyMiddleware} from "redux" import createSagaMiddleware from "redux-saga" import rootSaga from "./sagas" import Counter from "./Counter" import rootReducer from "./reducers" const sagaMiddleware = createSagaMiddleware() // 创建了一个saga中间件实例 // 下边这句话和下边的两行代码创建store的方式是一样的 // const store = createStore(reducers,applyMiddlecare(middlewares)) const createStoreWithMiddleware = applyMiddleware(middlewares)(createStore) const store = createStoreWithMiddleware(rootReducer) sagaMiddleware.run(rootSaga) const action = type => store.dispatch({ type }) function render() { ReactDOM.render(
action("INCREMENT")} onDecrement={() => action("DECREMENT")} onIncrementAsync={() => action("INCREMENT_ASYNC")} />, document.getElementById("root") ) } render() store.subscribe(render)
sagas.js
import { put, call, take,fork } from "redux-saga/effects"; import { takeEvery, takeLatest } from "redux-saga" export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); function* incrementAsync() { // 延迟 1s 在执行 + 1操作 yield call(delay, 1000); yield put({ type: "INCREMENT" }); } export default function* rootSaga() { // while(true){ // yield take("INCREMENT_ASYNC"); // yield fork(incrementAsync); // } // 下面的写法与上面的写法上等效 yield* takeEvery("INCREMENT_ASYNC", incrementAsync) }
reducer.js
export default function counter(state = 0, action) { switch (action.type) { case "INCREMENT": return state + 1 case "DECREMENT": return state - 1 case "INCREMENT_ASYNC": return state default: return state } }
从上面的代码结构可以看出,redux-saga的使用方式还是比较简单的,相比较之前的redux框架的CounterApp,多了一个sagas的文件,reducers文件还是之前的使用方式
redux-saga基本用法总结:
使用 createSagaMiddleware 方法创建 saga 的 Middleware ,然后在创建的 redux 的 store 时,使用 applyMiddleware 函数将创建的 saga Middleware 实例绑定到 store 上,最后可以调用 saga Middleware 的 run 函数来执行某个或者某些 Middleware 。
在 saga 的 Middleware 中,可以使用 takeEvery 或者 takeLatest 等 API 来监听某个 action ,当某个 action 触发后, saga 可以使用 call 发起异步操作,操作完成后使用 put 函数触发 action ,同步更新 state ,从而完成整个 State 的更新。
ok,故事到这里就接近尾声了,以上主要介绍了redux,react-redux和redux-saga目前redux全家桶主流的一些产品,接下来,主要会产出一下根据源码,手写一下redux和react-redux的轮子
希望各位大佬点赞,关注,鼓励一下,不足之处,还望斧正~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/6659.html
摘要:项目的架构也是最近在各种探讨研究。还求大神多指点项目技术总结技术栈项目结构探究初体验关于项目中的配置说明项目简单说明开发这一套,我个人的理解是体现的是代码分层职责分离的编程思想逻辑与视图严格区分。前端依旧使用技术栈完成。 项目地址:https://github.com/Nealyang/R...技术栈:react、react-router4.x 、 react-redux 、 webp...
摘要:背景项目用的是全家桶,之前有同事用改进了一波,一直都没去研究。这次的打算写一个输入框,输入拼音会返回对应的城市列表。 背景 项目用的是react全家桶, 之前有同事用redux-saga 改进了一波, 一直都没去研究。 前几天趁有空,也去学习了下, 写了个简单的demo练练手, 在这里简单分享一下。 这次的demo打算写一个输入框,输入拼音会返回对应的城市列表。并尽可能多的使用redu...
摘要:更多参考通过库掌握函数组件有些时候,我们想要动态的加载一些组件按需加载,比如在一个单页面应用中页面的组件页面的组件页面的组件只有真正要实例化当前页面的时候,才会去加载相应的组件。 react 前端项目技术选型、开发工具、周边生态 声明:这不是一篇介绍 React 基础知识的文章,需要熟悉 React 相关知识 主架构:react, react-router, redux, redux...
摘要:更多参考通过库掌握函数组件有些时候,我们想要动态的加载一些组件按需加载,比如在一个单页面应用中页面的组件页面的组件页面的组件只有真正要实例化当前页面的时候,才会去加载相应的组件。 react 前端项目技术选型、开发工具、周边生态 声明:这不是一篇介绍 React 基础知识的文章,需要熟悉 React 相关知识 主架构:react, react-router, redux, redux...
阅读 2486·2023-04-25 17:37
阅读 1163·2021-11-24 10:29
阅读 3676·2021-09-09 11:57
阅读 671·2021-08-10 09:41
阅读 2226·2019-08-30 15:55
阅读 2790·2019-08-30 15:54
阅读 1917·2019-08-30 15:53
阅读 877·2019-08-30 15:43