一篇文章总结redux、react-redux、redux-saga

本篇主要将react全家桶的产品非常精炼的提取了核心内容,精华程度堪比精油。各位大人,既然来了,客官您坐,来人,给客官看茶~~

redux

前言

首先,本篇文章要求您对js,react等知识有一定的了解,如果不曾了解,建议您先看一下:React精髓!一篇全概括(急速)

React有props和state:

  1. props意味着父级分发下来的属性
  2. state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,这就是react的单向数据流

这就意味着如果是一个数据状态非常复杂的应用,更多的时候发现React根本无法让两个组件互相交流,使用对方的数据,react的通过层级传递数据的这种方法是非常难受的,这个时候,迫切需要一个机制,把所有的state集中到组件顶部,能够灵活的将所有state各取所需的分发给所有的组件,是的,这就是redux

简介

  1. redux是的诞生是为了给 React 应用提供「可预测化的状态管理」机制。
  2. Redux会将整个应用状态(其实也就是数据)存储到到一个地方,称为store
  3. 这个store里面保存一棵状态树(state tree)
  4. 组件改变state的唯一方法是通过调用store的dispatch方法,触发一个action,这个action被对应的reducer处理,于是state完成更新
  5. 组件可以派发(dispatch)行为(action)给store,而不是直接通知其它组件
  6. 其它组件可以通过订阅store中的状态(state)来刷新自己的视图

使用步骤

  1. 创建reducer
    • 可以使用单独的一个reducer,也可以将多个reducer合并为一个reducer,即:combineReducers()
    • action发出命令后将state放入reucer加工函数中,返回新的state,对state进行加工处理
  2. 创建action
    • 用户是接触不到state的,只能有view触发,所以,这个action可以理解为指令,需要发出多少动作就有多少指令
    • action是一个对象,必须有一个叫type的参数,定义action类型
  3. 创建的store,使用createStore方法
    • store 可以理解为有多个加工机器的总工厂
    • 提供subscribe,dispatch,getState这些方法。

按步骤手把手实战。

上述步骤,对应的序号,我会在相关代码标出

 
  1. npm install redux -S // 安装

  2.  
  3. import { createStore } from 'redux' // 引入

  4.  
  5. const reducer = (state = {count: 0}, action) => {----------> ⑴

  6. switch (action.type){

  7. case 'INCREASE': return {count: state.count + 1};

  8. case 'DECREASE': return {count: state.count - 1};

  9. default: return state;

  10. }

  11. }

  12.  
  13. const actions = {---------->⑵

  14. increase: () => ({type: 'INCREASE'}),

  15. decrease: () => ({type: 'DECREASE'})

  16. }

  17.  
  18. const store = createStore(reducer);---------->⑶

  19.  
  20. store.subscribe(() =>

  21. console.log(store.getState())

  22. );

  23.  
  24. store.dispatch(actions.increase()) // {count: 1}

  25. store.dispatch(actions.increase()) // {count: 2}

  26. store.dispatch(actions.increase()) // {count: 3}

  27. 复制代码

自己画了一张非常简陋的流程图,方便理解redux的工作流程

 

 

 

react-redux

刚开始就说了,如果把store直接集成到React应用的顶层props里面,只要各个子组件能访问到顶层props就行了,比如这样:

 
  1. <顶层组件 store={store}>

  2. <App />

  3. </顶层组件>

  4. 复制代码

不就ok了吗?这就是 react-redux。Redux 官方提供的 React 绑定库。 具有高效且灵活的特性。

React Redux 将组件区分为 容器组件 和 UI 组件

  1. 前者会处理逻辑
  2. 后者只负责显示和交互,内部不处理逻辑,状态完全由外部掌控

两个核心

  • Provider

    看我上边那个代码的顶层组件4个字。对,你没有猜错。这个顶级组件就是Provider,一般我们都将顶层组件包裹在Provider组件之中,这样的话,所有组件就都可以在react-redux的控制之下了,但是store必须作为参数放到Provider组件中去

     
    1. <Provider store = {store}>

    2. <App />

    3. <Provider>

    4. 复制代码

     
    1. 这个组件的目的是让所有组件都能够访问到Redux中的数据。

    2. 复制代码

  • connect

    这个才是react-redux中比较难的部分,我们详细解释一下

    首先,先记住下边的这行代码:

     
    1. connect(mapStateToProps, mapDispatchToProps)(MyComponent)

    2. 复制代码

    mapStateToProps

    这个单词翻译过来就是把state映射到props中去 ,其实也就是把Redux中的数据映射到React中的props中去。

    举个栗子:

 
  1. const mapStateToProps = (state) => {

  2. return {

  3. // prop : state.xxx | 意思是将state中的某个数据映射到props中

  4. foo: state.bar

  5. }

  6. }

  7. 复制代码

然后渲染的时候就可以使用this.props.foo

 
  1. class Foo extends Component {

  2. constructor(props){

  3. super(props);

  4. }

  5. render(){

  6. return(

  7. // 这样子渲染的其实就是state.bar的数据了

  8. <div>this.props.foo</div>

  9. )

  10. }

  11. }

  12. Foo = connect()(Foo);

  13. export default Foo;

  14. 复制代码

然后这样就可以完成渲染了

mapDispatchToProps

这个单词翻译过来就是就是把各种dispatch也变成了props让你可以直接使用

 
  1. const mapDispatchToProps = (dispatch) => { // 默认传递参数就是dispatch

  2. return {

  3. onClick: () => {

  4. dispatch({

  5. type: 'increatment'

  6. });

  7. }

  8. };

  9. }

  10. 复制代码

 
  1. class Foo extends Component {

  2. constructor(props){

  3. super(props);

  4. }

  5. render(){

  6. return(

  7.  
  8. <button onClick = {this.props.onClick}>点击increase</button>

  9. )

  10. }

  11. }

  12. Foo = connect()(Foo);

  13. export default Foo;

  14. 复制代码

组件也就改成了上边这样,可以直接通过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

takeEvery就像一个流水线的洗碗工,过来一个脏盘子就直接执行后面的洗碗函数,一旦你请了这个洗碗工他会一直执行这个工作,绝对不会停止接盘子的监听过程和触发洗盘子函数

例如:每次点击 按钮去Fetch获取数据时时,我们发起一个 FETCH_REQUESTED 的 action。 我们想通过启动一个任务从服务器获取一些数据,来处理这个action,类似于

 
  1. window.addEventLister('xxx',fn)

  2. 复制代码

当dispatch xxx的时候,就会执行fn方法,

首先我们创建一个将执行异步 action 的任务(也就是上边的fn):

 
  1. // put:你就认为put就等于 dispatch就可以了;

  2.  
  3. // call:可以理解为实行一个异步函数,是阻塞型的,只有运行完后面的函数,才会继续往下;

  4. // 在这里可以片面的理解为async中的await!但写法直观多了!

  5. import { call, put } from 'redux-saga/effects'

  6.  
  7. export function* fetchData(action) {

  8. try {

  9. const apiAjax = (params) => fetch(url, params);

  10. const data = yield call(apiAjax);

  11. yield put({type: "FETCH_SUCCEEDED", data});

  12. } catch (error) {

  13. yield put({type: "FETCH_FAILED", error});

  14. }

  15. }

  16. 复制代码

然后在每次 FETCH_REQUESTED action 被发起时启动上面的任务,也就相当于每次触发一个名字为 FETCH_REQUESTED 的action就会执行上边的任务,代码如下

 
  1. import { takeEvery } from 'redux-saga'

  2.  
  3. function* watchFetchData() {

  4.  
  5. yield* takeEvery("FETCH_REQUESTED", fetchData)

  6. }

  7. 复制代码

注意:上面的 takeEvery 函数可以使用下面的写法替换

 
  1. function* watchFetchData() {

  2.  
  3. while(true){

  4. yield take('FETCH_REQUESTED');

  5. yield fork(fetchData);

  6. }

  7. }

  8. 复制代码

  • takeLatest

在上面的例子中,takeEvery 允许多个 fetchData 实例同时启动,在某个特定时刻,我们可以启动一个新的 fetchData 任务, 尽管之前还有一个或多个 fetchData 尚未结束

如果我们只想得到最新那个请求的响应(例如,始终显示最新版本的数据),我们可以使用 takeLatest 辅助函数

 
  1. import { takeLatest } from 'redux-saga'

  2.  
  3. function* watchFetchData() {

  4. yield* takeLatest('FETCH_REQUESTED', fetchData)

  5. }

  6. 复制代码

和takeEvery不同,在任何时刻 takeLatest 只允许执行一个 fetchData 任务,并且这个任务是最后被启动的那个,如果之前已经有一个任务在执行,那之前的这个任务会自动被取消

2、Effect Creators

redux-saga框架提供了很多创建effect的函数,下面我们就来简单的介绍下开发中最常用的几种

  • take(pattern)
  • put(action)
  • call(fn, ...args)
  • fork(fn, ...args)
  • select(selector, ...args)

take(pattern)

take函数可以理解为监听未来的action,它创建了一个命令对象,告诉middleware等待一个特定的action, Generator会暂停,直到一个与pattern匹配的action被发起,才会继续执行下面的语句,也就是说,take是一个阻塞的 effect

用法:

 
  1. function* watchFetchData() {

  2. while(true) {

  3. // 监听一个type为 'FETCH_REQUESTED' 的action的执行,直到等到这个Action被触发,才会接着执行下面的 yield fork(fetchData) 语句

  4. yield take('FETCH_REQUESTED');

  5. yield fork(fetchData);

  6. }

  7. }

  8. 复制代码

put(action)

put函数是用来发送action的 effect,你可以简单的把它理解成为redux框架中的dispatch函数,当put一个action后,reducer中就会计算新的state并返回,注意: put 也是阻塞 effect

用法:

 
  1. export function* toggleItemFlow() {

  2. let list = []

  3. // 发送一个type为 'UPDATE_DATA' 的Action,用来更新数据,参数为 `data:list`

  4. yield put({

  5. type: actionTypes.UPDATE_DATA,

  6. data: list

  7. })

  8. }

  9. 复制代码

call(fn, ...args)

call函数你可以把它简单的理解为就是可以调用其他函数的函数,它命令 middleware 来调用fn 函数, args为函数的参数,注意: fn 函数可以是一个 Generator 函数,也可以是一个返回 Promise 的普通函数,call 函数也是阻塞 effect

用法:

 
  1. export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

  2.  
  3. export function* removeItem() {

  4. try {

  5. // 这里call 函数就调用了 delay 函数,delay 函数为一个返回promise 的函数

  6. return yield call(delay, 500)

  7. } catch (err) {

  8. yield put({type: actionTypes.ERROR})

  9. }

  10. }

  11. 复制代码

fork(fn, ...args)

fork 函数和 call 函数很像,都是用来调用其他函数的,但是fork函数是非阻塞函数,也就是说,程序执行完 yield fork(fn, args) 这一行代码后,会立即接着执行下一行代码语句,而不会等待fn函数返回结果后,在执行下面的语句

用法:

 
  1. import { fork } from 'redux-saga/effects'

  2.  
  3. export default function* rootSaga() {

  4. // 下面的四个 Generator 函数会一次执行,不会阻塞执行

  5. yield fork(addItemFlow)

  6. yield fork(removeItemFlow)

  7. yield fork(toggleItemFlow)

  8. yield fork(modifyItem)

  9. }

  10. 复制代码

select(selector, ...args)

select 函数是用来指示 middleware调用提供的选择器获取Store上的state数据,你也可以简单的把它理解为redux框架中获取store上的 state数据一样的功能 :store.getState()

用法:

 
  1. export function* toggleItemFlow() {

  2. // 通过 select effect 来获取 全局 state上的 `getTodoList` 中的 list

  3. let tempList = yield select(state => state.getTodoList.list)

  4. }

  5. 复制代码

一个具体的实例

**index.js **

 
  1. import React from 'react';

  2. import ReactDOM from 'react-dom';

  3. import {createStore, applyMiddleware} from 'redux'

  4. import createSagaMiddleware from 'redux-saga'

  5.  
  6. import rootSaga from './sagas'

  7. import Counter from './Counter'

  8. import rootReducer from './reducers'

  9.  
  10. const sagaMiddleware = createSagaMiddleware() // 创建了一个saga中间件实例

  11.  
  12. // 下边这句话和下边的两行代码创建store的方式是一样的

  13. // const store = createStore(reducers,applyMiddlecare(middlewares))

  14.  
  15. const createStoreWithMiddleware = applyMiddleware(middlewares)(createStore)

  16. const store = createStoreWithMiddleware(rootReducer)

  17.  
  18. sagaMiddleware.run(rootSaga)

  19.  
  20. const action = type => store.dispatch({ type })

  21.  
  22. function render() {

  23. ReactDOM.render(

  24. <Counter

  25. value={store.getState()}

  26. onIncrement={() => action('INCREMENT')}

  27. onDecrement={() => action('DECREMENT')}

  28. onIncrementAsync={() => action('INCREMENT_ASYNC')} />,

  29. document.getElementById('root')

  30. )

  31. }

  32.  
  33. render()

  34.  
  35. store.subscribe(render)

  36. 复制代码

sagas.js

 
  1. import { put, call, take,fork } from 'redux-saga/effects';

  2. import { takeEvery, takeLatest } from 'redux-saga'

  3.  
  4. export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

  5.  
  6. function* incrementAsync() {

  7. // 延迟 1s 在执行 + 1操作

  8. yield call(delay, 1000);

  9. yield put({ type: 'INCREMENT' });

  10. }

  11.  
  12. export default function* rootSaga() {

  13. // while(true){

  14. // yield take('INCREMENT_ASYNC');

  15. // yield fork(incrementAsync);

  16. // }

  17.  
  18. // 下面的写法与上面的写法上等效

  19. yield* takeEvery("INCREMENT_ASYNC", incrementAsync)

  20. }

  21. 复制代码

reducer.js

 
  1. export default function counter(state = 0, action) {

  2. switch (action.type) {

  3. case 'INCREMENT':

  4. return state + 1

  5. case 'DECREMENT':

  6. return state - 1

  7. case 'INCREMENT_ASYNC':

  8. return state

  9. default:

  10. return state

  11. }

  12. }

  13. 复制代码

从上面的代码结构可以看出,redux-saga的使用方式还是比较简单的,相比较之前的redux框架的CounterApp,多了一个sagas的文件,reducers文件还是之前的使用方式

redux-saga基本用法总结:

  1. 使用 createSagaMiddleware 方法创建 saga 的 Middleware ,然后在创建的 redux 的 store 时,使用 applyMiddleware 函数将创建的 saga Middleware 实例绑定到 store 上,最后可以调用 saga Middleware 的 run 函数来执行某个或者某些 Middleware 。
  2. 在 saga 的 Middleware 中,可以使用 takeEvery 或者 takeLatest 等 API 来监听某个 action ,当某个 action 触发后, saga 可以使用 call 发起异步操作,操作完成后使用 put 函数触发 action ,同步更新 state ,从而完成整个 State 的更新。

ok,故事到这里就接近尾声了,以上主要介绍了redux,react-redux和redux-saga目前redux全家桶主流的一些产品,接下来,主要会产出一下根据源码,手写一下redux和react-redux的轮子


转载:https://juejin.im/post/5ce0ae0c5188252f5e019c2c

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
react-native-redux-router是一个用于在React Native应用中管理路由和状态的库。它结合了React Native、Redux和React Navigation,提供了一种方便的方式来处理应用程序的导航和状态管理。 下面是一个简单的示例,演示了如何在React Native应用中使用react-native-redux-router: 1. 首先,安装所需的依赖项。在终端中运行以下命令: ```shell npm install react-native react-redux redux react-navigation react-native-router-flux --save ``` 2. 创建一个Redux store,并将其与React Native应用程序的根组件连接起来。在App.js文件中,添加以下代码: ```javascript import React from 'react'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import rootReducer from './reducers'; import AppNavigator from './navigation/AppNavigator'; const store = createStore(rootReducer); export default function App() { return ( <Provider store={store}> <AppNavigator /> </Provider> ); } ``` 3. 创建一个导航器组件,并定义应用程序的导航结构。在navigation/AppNavigator.js文件中,添加以下代码: ```javascript import { createAppContainer } from 'react-navigation'; import { createStackNavigator } from 'react-navigation-stack'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { Actions } from 'react-native-router-flux'; import HomeScreen from '../screens/HomeScreen'; import DetailsScreen from '../screens/DetailsScreen'; const MainNavigator = createStackNavigator({ Home: { screen: HomeScreen }, Details: { screen: DetailsScreen }, }); const AppNavigator = createAppContainer(MainNavigator); const mapStateToProps = state => ({ // 将Redux状态映射到导航器组件的props中 }); const mapDispatchToProps = dispatch => bindActionCreators(Actions, dispatch); export default connect(mapStateToProps, mapDispatchToProps)(AppNavigator); ``` 4. 创建屏幕组件,并在其中使用导航和Redux状态。在screens/HomeScreen.js文件中,添加以下代码: ```javascript import React from 'react'; import { View, Text, Button } from 'react-native'; import { Actions } from 'react-native-router-flux'; const HomeScreen = () => { return ( <View> <Text>Welcome to the Home Screen!</Text> <Button title="Go to Details" onPress={() => Actions.details()} /> </View> ); } export default HomeScreen; ``` 5. 创建其他屏幕组件,并在其中使用导航和Redux状态。在screens/DetailsScreen.js文件中,添加以下代码: ```javascript import React from 'react'; import { View, Text, Button } from 'react-native'; import { Actions } from 'react-native-router-flux'; const DetailsScreen = () => { return ( <View> <Text>Welcome to the Details Screen!</Text> <Button title="Go back" onPress={() => Actions.pop()} /> </View> ); } export default DetailsScreen; ``` 这是一个简单的示例,演示了如何在React Native应用中使用react-native-redux-router来管理路由和状态。你可以根据自己的需求进行扩展和定制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值