您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net:境况管理之Redux的利用⑤异步操作

新葡亰496net:境况管理之Redux的利用⑤异步操作

发布时间:2019-12-12 07:59编辑:新葡亰官网浏览(126)

    你需要 Mobx 还是 Redux ?

    2018/02/22 · JavaScript · mobx, Redux

    原文出处: 熊建刚的博客   

    在过去一年,越来越多的项目继续或者开始使用React和Redux开发,这是目前前端业内很普遍的一种前端项目解决方案,但是随着开发项目越来越多,越来越多样化时,个人又有了不同的感受和想法。是不是因为已经有了一个比较普遍的,熟悉的项目技术栈,我们就一直完全沿用呢,有没有比他更适合的方案呢?恰逢团队最近有一个新项目,于是博主开始思考,有没有可能使用其他可替代技术开发呢?既能提高开发效率,又能拓展技术储备和眼界,经过调研,选择了Mobx,最终使用React Mobx搭建了新项目,本篇总结分享从技术选型到项目实现的较完整过程,希望共同进步。

    索引

    • 1 前言
    • 2 状态管理
    • 3 Mobx VS Redux
      • 3.1 Redux
      • 3.2 Mobx
      • 3.3 函数式和面向对象
      • 3.4 单一store和多store
      • 3.5 JavaScript对象和可观察对象
      • 3.6 不可变(Immutable)和可变(Mutable)
      • 3.7 mobx-react和react-redux
    • 4 选择Mobx的原因
    • 5 不选择Mobx的可能原因
    • 6 代码对比
      • 6.1 架构
      • 6.2 注入Props
      • 6.3 定义Action/Reducer等
      • 6.4 异步Action
    • 7 一些想法
    • 8 参考

    新葡亰496net 1

    Redux is a terribly simple library for state management, and has made working with React more manageable for everyone. 

    新葡亰496net 2

    前言

    当我们使用React开发web应用程序时,在React组件内,可以使用this.setState()this.state处理或访问组件内状态,但是随着项目变大,状态变复杂,通常需要考虑组件间通信问题,主要包括以下两点:

    1. 某一个状态需要在多个组件间共享(访问,更新);
    2. 某组件内交互需要触发其他组件的状态更新;

    关于这些问题,React组件开发实践推荐将公用组件状态提升:

    Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor

    通常多组件需要处理同一状态,我们推荐将共享状态提升至他们的共同最近祖先组件内。更多详情查看

    当项目越发复杂时,我们发现仅仅是提升状态已经无法适应如此复杂的状态管理了,程序状态变得比较难同步,操作,到处是回调,发布,订阅,这意味着我们需要更好的状态管理方式,于是就引入了状态管理库,如Redux,Mobx,Jumpsuit,Alt.js等。

    title: 翻译|A Dummy’s Guide to Redux and Thunk in React
    date: 2017-04-15 21:36:07
    categories: 翻译
    tags: Redux

    一、什么情况需要redux?

    1、用户的使用方式复杂

    2、不同身份的用户有不同的使用方式(比如普通用户和管理员)

    3、多个用户之间可以协作

    4、与服务器大量交互,或者使用了WebSocket

    5、View要从多个来源获取数据

    简单说,如果你的UI层非常简单,没有很多互动,Redux就是不必要的,用了反而增加复杂性。多交互、多数据源的场景就比较适合使用Redux。

    1、Web应用是一个状态机,视图与状态是一一对应的。

    2、所有的状态,保存在一个对象里。

    Redux是一个简单的状态管理库,可以让React状态管理变得更加容易。

    本教程总共6篇,每日更新一篇,请关注我们!你可以进入历史消息查看以往文章,也敬请期待我们的新文章!

    状态管理

    状态管理库,无论是Redux,还是Mobx这些,其本质都是为了解决状态管理混乱,无法有效同步的问题,它们都支持:

    1. 统一维护管理应用状态;
    2. 某一状态只有一个可信数据来源(通常命名为store,指状态容器);
    3. 操作更新状态方式统一,并且可控(通常以action方式提供更新状态的途径);
    4. 支持将store与React组件连接,如react-reduxmobx-react;通常使用状态管理库后,我们将React组件从业务上划分为两类:
      1. 容器组件(Container Components):负责处理具体业务和状态数据,将业务或状态处理函数传入展示型组件;
      2. 展示型组件(Presentation Components):负责展示视图,视图交互回调内调用传入的处理函数;

    三、Redux的工作流程

    新葡亰496net 3Redux工作流程

    首先,用户发出Action

    store.dispatch;

    然后,Store自动调用Reducer,并且传入两个参数:当前State和收到的Action。Reducer会返回新的State。

    let nextState = todoApp (previousState , action);

    State一旦变化,Store就会调用监听函数。

    // 设置监听函数

    store.subscribe;

    lisrener 可以通过store.getState()得到当前状态。如果使用的是React,这时可以触发重新渲染View。

    function listener () {

    let nextState=store.getState();

    component.setState( newState );

    }

    如果现在没理解以上流程,不要急,看完以下API就差不多能动的Redux的核心机制啦。

    However, there are a lot of cases where people blindly follow boilerplate code to integrate redux with their React application without understanding all the moving parts involved.

    1、React第三方组件5(状态管理之Redux的使用①简单使用)---2018.03.20

    Mobx VS Redux

    目前来看,Redux已是React应用状态管理库中的霸主了,而Mobx则是一方诸侯,我们为什么要选择Mobx,而不是继续沿用Redux呢,那就需要比较他们的异同了。

    Mobx和Redux都是JavaScript应用状态管理库,都适用于React,Angular,VueJs等框架或库,而不是局限于某一特定UI库。

    Redux和Thunk的使用傻瓜教程

    原文参见
    Github repo
    强烈推荐这篇文章.

    如果你和我一样,读了Redux的文档,看了Dan的录像,Wes的课程,仍然不能抓住怎么使用Redux的核心,希望这个傻瓜教程能够帮到你.
    在真正实施之前,我做了一些尝试,所以我决定写一点从已经存在的使用fetch JSON数据的app一步步转变为使用Redux和Redux Thunk的app.如果你不知道Thunk是什么,也不要担心,我们会在”Redux 之路”部分来使用它执行函数的异步调用.
    这个教程需要你对React和ES6/2015有基本的掌握.

    四、Redux API

    然而,很多情况下,人们盲目遵循示例代码,将redux与其React应用程序集成在一起,而并不了解引入的所有组件。

    2、React第三方组件5(状态管理之Redux的使用②TodoList上)---2018.03.21

    Redux

    要介绍Redux,我们就不得不谈到Flux了:

    Flux is the application architecture that Facebook uses for building client-side web applications.It’s more of a pattern rather than a formal framework

    Flux是Facebook用来开发客户端-服务端web应用程序的应用架构,它更多是一种架构模式,而非一个特定框架。详解Flux。

    而Redux更多的是遵循Flux模式的一种实现,是一个JavaScript库,它关注点主要是以下几方面:

    1. Action:一个JavaScript对象,描述动作相关信息,主要包含type属性和payload属性:
      1. type:action 类型;
      2. payload:负载数据;
    2. Reducer:定义应用状态如何响应不同动作(action),如何更新状态;
    3. Store:管理action和reducer及其关系的对象,主要提供以下功能:
      1. 维护应用状态并支持访问状态(getState());
      2. 支持监听action的分发,更新状态(dispatch(action));
      3. 支持订阅store的变更(subscribe(listener));
    4. 异步流:由于Redux所有对store状态的变更,都应该通过action触发,异步任务(通常都是业务或获取数据任务)也不例外,而为了不将业务或数据相关的任务混入React组件中,就需要使用其他框架配合管理异步任务流程,如redux-thunkredux-saga等;

    非Redux方法

    components/ItemList.js中创建一个React组件,用于fetch和显示items列表.

    store

    Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个Store。

    Redux提供createStore这个函数,用来生成Store。

    下面代码中,createStore函数接受另一个函数作为参数,返回新生成的Store对象。

    import { createStore } from 'redux';

    const store = createStore ;

    Store 对象包含所有的数据,如果想得到某个时点的数据,就要对Store生成快照。这种时点的数据集合,就叫做State。当前此刻的State,可以通过store.getState()拿到。

    import { crateStore } from 'redux';

    const store = createStore ;

    const state = store.getState();

    Redux 规定,一个State对应一个View。只要State相同,View就相同,你知道State,就知道View是什么样,反之亦然。

    State的变化,会导致VIew 的变化。但是,用户接触不到State,只能接触到View。所以,State的变化必须是VIew导致的。

    Action就是View发出的通知,表示state应该要发生变化啦。

    Action是一个对象,其中的type属性是必须的,表示Action的名称。其他属性可以自由设置。

    const action = {

    type : 'ADD_TODO',

    payload ; 'learn Redux'

    };

    上述代码中,Action的名称是ADD_TODO,它携带的信息是字符串Learn Redux。

    可以这样理解,Action描述当前发生的事情。改变State的唯一办法,就是使用Action,它会运送数据到Store。

    view 要发送多少种信息,就会有多少种Action。如果都手写,会很麻烦。可以定义一个函数来生成Action,这个函数就叫做ActionCreator。

    const ADD_TODO = '添加TODO';

    function addTodo {

    return {

    type: ADD_TODO,

    text

    }

    }

    const action = addTodo(' Learn Redux');

    store.dispatch()是View发出Action的唯一方法。

    import { createStore } from 'redux';

    const store=createStore ;

    store.dispatch({

    type : 'ADD_TODO';

    payload : 'Learn Redux'

    });

    上面的代码中,store.diapatch接受了一个Action对象作为参数,将它发出去,结合ActionCreator,这段代码可以改写如下。

    import { createStore } from 'redux';

    const store=createStore ;

    store.dispatch(add Todo('Learn Rudux'));

    const ADD_TODO = '添加TODO';

    function addTodo {

    return {

    type: ADD_TODO,

    text

    }

    }

    Store 收到Action以后,必须给出一个新的State,这样View才会发生变化。这种State的计算过程就叫做Reducer。Ruducer是一个函数,他接受Action和当前State作为参数,返回一个新的State,下面是一个实际的例子

    const defaultState = 0;

    const reducer = (state=defaultState,action)=>{

    switch (action.type){

    case 'ADD';

    return state action.payload;

    default:

    return state;

    }

    };

    const state = reducer (1, {

    type : 'ADD',

    payload: 2

    });

    上面代码中,reducer函数收到名为ADD的Action以后,就返回一个新的State,作为加法的计算结果。其他运算的逻辑,也可以根据Action的不同来实现。

    实际应用中,Reducer函数不用像上面那样手动调用,store.dispatch方法会触发Reudcer自动执行。为此,Store需要知道ReduCer函数,做法就是在生成Store的时候,将Reducer传入createStore方法。

    import {createStore } from 'redux';

    const store = createStore;

    store.dispatch(addTodo('Learn Redux'));

    const ADD_TODO='添加Todo';

    function addTodo{

    return {

    type:ADD_TODO,

    text

    }

    }

    上面代码中,createStore接受Reducer作为参数,生成一个新的Store,以后每当store.dispatch发过来一个新的Action,就会自动调用Reducer,得到新的State。

    Store允许使用store.subscribe方法设置监听函数,一旦State发生变化,就自动执行这个函数。

    import { createStore } from 'redux';

    const store = createStore ;

    store.dispatch(addTodo('Learn Redux'));

    const ADD_TODO='添加Todo';

    function addTodo{

    return {

    type:ADD_TODO,

    text

    }

    }

    store.subscribe;

    显然,只要把View的更新函数(对于React项目,就是组件的render方法和setState方法)放入listen,就会实现view的自动渲染。

    store.subscribe方法返回一个函数,调用这个函数就可以解除监听。

    let unsubscribe = store.subscribe =>

    console.log(store.getState

    };

    unsubscribe();

    · React and redux on their own (React和redux本身是独立的)

    At this point it’s hard for some to believe, but redux and React are actually two separate libraries which can and have been used completely independent of each other. Lets take a look at redux’s state management flow :

    在这一点上,它很难让人相信,但事实上这两个库的确都能独立使用。让我们花点时间看看redux状态管理流:

    新葡亰496net 4

    reudx状态管理流

    If you have worked with redux before, you know that its functionality revolves around a “store”, which is where the state of the application lives. 

    如果你以前用过redux,你应该知道它的功能围绕store,也就是应用程序状态保存的地方。

    There is no way anyone can directly modify the store. The only way to do so is through reducers, and the only way to trigger reducers is to dispatch actions. So ultimately :

    store是不能直接修改的。唯一只能通过reducers修改,而触发reducers要通过发送actions。总而言之:

    * - To change data, we need to dispatch an action (要改变数据,我们只能通过发送action)*

    On the other hand, when we want to retrieve data, we do not get it directly from the store. 

    另一方面,当我们想去获取数据时,我们不能直接从store获得。

    Instead, we get a snapshot of the data in the store at any point in time using store.getState(), which gives us the “state” of the application as on the time at which we called the getState method.

    作为替代,我们能通过store的getState()方法得到store中任一时刻的数据,就像某一时刻定格的照片。当我们调用getState方法时,我们就能获得应用程序那个时刻的状态。总而言之:

     - To obtain data we need to get the current state of the store (要获取数据,我们就要获得store中当前的状态)

    Now, let’s come to the (simplified) component structure of a standard react todo-mvc application:

    现在,让我们来看看标准的react Todo-mvc应用结构:

    新葡亰496net 5

    标准的react Todo-mvc应用

    3、React第三方组件5(状态管理之Redux的使用③TodoList中)---2018.03.22

    Mobx

    Mobx是一个透明函数响应式编程(Transparently Functional Reactive Programming,TFRP)的状态管理库,它使得状态管理简单可伸缩:

    Anything that can be derived from the application state, should be derived. Automatically.

    任何起源于应用状态的数据应该自动获取。

    其原理如图:

    新葡亰496net 6

    1. Action:定义改变状态的动作函数,包括如何变更状态;
    2. Store:集中管理模块状态(State)和动作(action);
    3. Derivation(衍生):从应用状态中派生而出,且没有任何其他影响的数据,我们称为derivation(衍生),衍生在以下情况下存在:
      1. 用户界面;
      2. 衍生数据;衍生主要有两种:
        1. Computed Values(计算值):计算值总是可以使用纯函数(pure function)从当前可观察状态中获取;
        2. Reactions(反应):反应指状态变更时需要自动发生的副作用,这种情况下,我们需要实现其读写操作;

    import {observable, autorun} from 'mobx'; var todoStore = observable({ /* some observable state */ todos: [], /* a derived value */ get completedCount() { return this.todos.filter(todo => todo.completed).length; } }); /* a function that observes the state */ autorun(function() { console.log("Completed %d of %d items", todoStore.completedCount, todoStore.todos.length ); }); /* ..and some actions that modify the state */ todoStore.todos[0] = { title: "Take a walk", completed: false }; // -> synchronously prints: 'Completed 0 of 1 items' todoStore.todos[0].completed = true; // -> synchronously prints: 'Completed 1 of 1 items'

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    import {observable, autorun} from 'mobx';
     
    var todoStore = observable({
        /* some observable state */
        todos: [],
     
        /* a derived value */
        get completedCount() {
            return this.todos.filter(todo => todo.completed).length;
        }
    });
     
    /* a function that observes the state */
    autorun(function() {
        console.log("Completed %d of %d items",
            todoStore.completedCount,
            todoStore.todos.length
        );
    });
     
    /* ..and some actions that modify the state */
    todoStore.todos[0] = {
        title: "Take a walk",
        completed: false
    };
    // -> synchronously prints: 'Completed 0 of 1 items'
     
    todoStore.todos[0].completed = true;
    // -> synchronously prints: 'Completed 1 of 1 items'

    罗列一下基础组件

    首先我们使用包含各种items的state配置一个静态的组件,2 个boolean state分别用于根据loading和error来渲染出各自的单独显示组件.

     import React, { Component } from 'react';
    class ItemList extends Component {
      constructor() {
          super();
          this.state = {
              items: [//items在列表中现实的内容
                  {
                      id: 1,
                      label: 'List item 1'
                  },
                  {
                      id: 2,
                      label: 'List item 2'
                  },
                  {
                      id: 3,
                      label: 'List item 3'
                  },
                  {
                      id: 4,
                      label: 'List item 4'
                  }
              ],
              hasErrored: false, //网络请求错误的状态
              isLoading: false   //网络请求中的状态
          };
      }
      render() {
          if (this.state.hasErrored) {//根据Errored的状态决
          //定是否加载这个组件,网络请求错误时,false=>true
              return <p>Sorry! There was an error loading the items</p>;
          }
          if (this.state.isLoading) {
          //网络请求中的组件,发出请求时,false=>true
              return <p>Loading…</p>;
          }
          return (
              <ul>
                  {this.state.items.map((item) => (
                      <li key={item.id}>
                          {item.label}
                      </li>
                  ))}
              </ul>
          );
      }
    }
    export default ItemList;
    

    可能看起来还不是特别能说明问题,但是已经是一个好的开端了.
    渲染的时候,组件输出四个items列表,但是如果你把isLoadinghasError的state由false改为false的时候,对应的<p></p>就会显示出来.(注意每个组件都是return出来的,每次只显示一个).

    五、中间件与异步操作

    一个关键的问题没有解决:异步操作怎么办?Action发出以后,Reducer立即算出State,这叫同步;Action发出以后,过一段时间再执行Reducer,这叫异步。

    怎么才能Reducer在异步操作结束后自动执行呢?这就要用到新的工具:中间件(middleware)

    新葡亰496net 7middleware

    为了理解中间件,然我们站在框架作者的角度思考问题:如果要添加功能,你会在那个环节添加?

    Reducer:纯函数,只承担计算State的功能,不适合承担其他功能,也承担不了,因为理 论上,纯函数不能进行读写操作。

    View :与State一一对应,可以看作是State的视觉层,也不合适承担其他功能。

    Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。

    想来想去,只有发送Action的这个步骤,即store.dispatch()方法,可以添加功能

    这里只介绍如何使用中间件

    import { applyMIddleware , crateStore } from 'redux';

    import createLogger from 'redux-logger';

    const logger= createLogger();

    const store = createStore (

    reducer,

    applyMiddleware

    );

    上面代码中,redux-logger提供了一个生成器createLogger,可以生出日志中间件logger。然后将它放在applyMiddleware方法之中,传入createState方法,就完成了store.dispatch()的功能增强。

    这里有两点需要注意:

    createStore方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就 是第三个参数了。

    import { applyMIddleware , crateStore } from 'redux';

    import createLogger from 'redux-logger';

    const logger= createLogger();

    const store = createStore(

    reducer,

    initial_state,

    applyMiddleware

    );

    中间件的次序有讲究

    const store = createStore(

    reducer,

    applyMiddleware(thunk,promise,logger)

    );

    上面代码中,applyMiddleware方法的三个参数,就是三个中间件。有的中间件有次序要求,使用前要查一下文档。比如,logger就一定要放到最后,否则输出结果会不正确。

    · Putting them together (把react和redux结合起来)

    If we want to link our React application with the redux store, we first have to let our app know that this store exists. This is where we come to the first major part of the react-redux library, which is the Provider.

    如果我们想将React应用和redux store连接起来,首先要让我们的应用知道store的存在。这是我们要迈过的第一步,它就是react-redux库中提供的Provider组件。

    Provider is a React component given to us by the “react-redux” library. It serves just one purpose : to “provide” the store to its child components.

    Provider是一个由react-redux库提供的React组件。它的作用只有一个目的:将store传给它的子组件。

    // This is the store we create with redux's createStore

    const store = createStore(todoApp,{})

    // Provider is given the store as a prop

    render( <Provider store={store}>  <App/> </Provider> , document.getElementById('app-node'))

    Since the provider only makes the store accessible to it’s children, and we would ideally want our entire app to access the store, the most sensible thing to do would be to put our App component within Provider.

    由于provider只让store对于它的孩子可访问,我们的理想是想让整个app访问store,最可行的方法就是将我们的App组件放进Provider中。

    If we were to follow the previous diagram, the Provider node would be represented as a parent node on top of the App node. However, because of the utility that Provider gives us, I feel it’s more appropriate to represent it as something which “wraps” the entire application tree, like this :

    如果按照我们接着之前的描述,则Provider和App节点被当作父子节点。然而,鉴于Provider组件发挥的作用,我觉得它更恰当的应该被描述为包裹整个应用的存在。就像这样:

    新葡亰496net 8

    Provider包含了整个应用

    4、React第三方组件5(状态管理之Redux的使用④TodoList下)---2018.03.23

    函数式和面向对象

    Redux更多的是遵循函数式编程(Functional Programming, FP)思想,而Mobx则更多从面相对象角度考虑问题。

    Redux提倡编写函数式代码,如reducer就是一个纯函数(pure function),如下:

    (state, action) => { return Object.assign({}, state, { ... }) }

    1
    2
    3
    4
    5
    (state, action) => {
      return Object.assign({}, state, {
        ...
      })
    }

    纯函数,接受输入,然后输出结果,除此之外不会有任何影响,也包括不会影响接收的参数;对于相同的输入总是输出相同的结果。

    Mobx设计更多偏向于面向对象编程(OOP)和响应式编程(Reactive Programming),通常将状态包装成可观察对象,于是我们就可以使用可观察对象的所有能力,一旦状态对象变更,就能自动获得更新。

    改为动态值

    直接编码items对于组件来说不是特别有用,所以最好从JSON API来fetch items数据,如果这样做的话,我们就可以把isLoadinghasError改为合适的状态.
    响应值和我们直接编码是一样,但是在实际生产中,你可能会拉回一个图书畅销榜的列表,最新的blog帖子或者其他app中需要的内容.
    为了fetch items,我们将使用合适的Fetch API.Fetch使得执行请求比传统的XMLHttpRequest更容易,并且返回的是响应值的promise对象(这一点对于Thunk很重要).Fetch并不是在所有的浏览器中都可以使用,所以你需要在项目中添加依赖项.

     npm install whatwg-fetch --save
    

    转变实际上相当简单.

    • 首先把items的初始化state设置为空数组
    • 现在我们添加一个方法fetch数据,同时还要设定loading和error的状态.
    fetchData(url) {//fetch的包装方法
      //进入函数首先设定isLoading state false=>true
      this.setState({ isLoading: true });
      fetch(url)
          .then((response) => {//返回promise独享
              if (!response.ok) {
                  throw Error(response.statusText);
              }
              //不管返回的数据是什么,只要返回数据
              //就修改isLoading state true=>false
              this.setState({ isLoading: false });
              return response;
          })
          .then((response) => response.json())
          .then((items) => this.setState({ items })) 
          // ES6 property value shorthand for { items: items }
          .catch(() => this.setState({ hasErrored: true }));//返回数据的解析为json,如果捕获到错误就hasErrored:
          // false=>true
    }
    
    • 函数写完以后,在组件加载的时候就调用函数
     componentDidMount() {
     this.fetchData('http://      5826ed963900d612000138bd.mockapi.io/items');
    }
    

    完成以后,代码如下

     class ItemList extends Component {
       constructor() {
           this.state = {
               items: [],
           };
       }
       fetchData(url) {
           this.setState({ isLoading: true });
           fetch(url)
               .then((response) => {
                   if (!response.ok) {
                       throw Error(response.statusText);
                   }
                   this.setState({ isLoading: false });
                   return response;
               })
               .then((response) => response.json())
               .then((items) => this.setState({ items }))
               .catch(() => this.setState({ hasErrored: true }));
       }
       componentDidMount() {
           this.fetchData('http://5826ed963900d612000138bd.mockapi.io/items');
       }
       render() {
       }
    }
    

    差不多了,组件现在从REST API fetch items数据,在4个items到达之前,你希望看”Loading...”出现提示.如果URL不能返回数据,你应该看到error 的信息.

    然而(译注:讨厌的然而),在现实中,组件不应该包含具体的fetch逻辑,data也不应该储存在组件的state中,所以Redux实时的出现了.

    六、异步操作的基本思路

    同步操作只要发出一种Action即可,异步操作的差别是他要发出三种Action。

    1)操作发起时的Action

    2)操作成功时的Action

    3)操作失败时的Action

    以向服务器取出数据为例,三种Action可以有两种不同的写法

    // 写法一:名称相同,参数不同

    { type : 'FETCH_POSTS' }

    { type: 'FETCH_POSTS', status: 'error', error: 'Oops' }

    { type: 'FETCH_POSTS', status: 'success', response: { ... } }

    // 写法二:名称不同

    { type: 'FETCH_POSTS_REQUEST' }

    { type: 'FETCH_POSTS_FAILURE', error: 'Oops' }

    { type: 'FETCH_POSTS_SUCCESS', response: { ... } }

    除了Action种类不同,同步操作的State也要进行改造,反映不同的操作状态,下面是State的一个例子

    let state = {

    isFetching : true,

    didInvalidate : true,

    lastUpdated: 'xxxx'

    };

    上面代码中,State的属性isFetching表示是否在抓取数据,didInvalidate表示数据是否过时,lastUpdated表示上一次更新的时间

    现在,整个异步操作的思路清楚啦。

    1)操作开始时,送出一个Action,触发State更新为“正在操作”状态,View重新渲染

    2)操作结束后,再送出一个Action,触发State更新为“操作结束”状态,View再一次重新渲染

    异步操作至少要送出两个Action:用户触发第一个Action,这个跟同步一样,没有问题;如何才能在操作结束时,系统自动送出第二个Action呢?

    奥妙就在Action Creator之中。

    class AsyncApp extends Component {

    componentDidMount() {

    const { dispatch,selectedPost } =this.props

    dispatch (fetchPosts(selectedPost))

    }

    }

    上面代码是一个异步组件的例子,加载成功后(componentDidMouth方法),他送出了(dispatch方法)一个Action,向服务器要求数据fetchPosts(selectedSUbredddit).这里的fetchPosts就是Action Creator

    const fetchPosts = postTitle => ( dispatch , getState) => {

    dispatch ( requestPosts( postTitle));

    return fetch ( '/some/API/${postTitle}.json')

    .then (response = > response.json

    .then( json=> dispatch(receivePosts(postTitle,json)));

    };

    };

    // 使用方法一

    store.dispatch(fetchPosts);

    // 使用方法二

    store.dispatch ( fetchPosts ).then=>

    console.log(store.getState

    );

    上面代码中,fetchPosts是一个Action Creator,返回一个函数,这个函数执行后,先发出一个Action(requestPosts(postTitle)),然后进行操作。拿到结果后,先将结果转换为JSON格式,然后再发出一个Action(recervePosts(postTitle,json)).

    上面代码中,有几个地方需要注意。

    fetchPosts返回一个函数,而普通的Action Creator默认返回一个对象

    返回的函数的参数是disPatchgetState这两个Redux方法,普通的ActionCreator的参数 是Action的内容。

    在返回的函数之中,先发出一个Action(requestPosts(postTitle)),表示操作开始

    异步操作结束之后,再发出一个Action(ReceivePosts(postTitle,json)),表示操作结束

    这样的处理,就解决了自动发送第二个Action的问题,但是又带来一个新的问题,Action是由store.dispatch方法发送的。这时,就要使用中间件redux-thunk

    import { createStore, applyMiddware } from 'redux';

    import thunk form 'redux-thunk';

    import reducer from './reducers';

    const store = creareStore(

    reducer,

    applyMiddleware

    };

    上面代码使用redux-thunk中间件,改造store.dispatch,使的后者可以接受函数作为参数。因此,异步操作的第一种解决方案就是,写一个返回函数的Action Creator,然后使用redux-thunk中间件改造store.dispatch.

    · Connect 

    Now that we have “provided” the redux store to our application, we can now connect our components to it. 

    既然我们已经用Provider组件把redux store提供给了我们的应用,那我们现在就把组件和它连接起来。

    We established previously that there is no way to directly interact with the store. 

    我们之前建立的应用,是不能直接和store交互的。

    We can either retrieve data by obtaining its current state, or change its state by dispatching an action (we only have access to the top and bottom component of the redux flow diagram shown previously).

    我们只能通过它的当前状态获取数据,或者通过发送一个action去改变它的状态。(我们只能用之前的redux流程图中的顶部和底部的组件)

    This is precisely what connect does. Consider this piece of code, which uses connect to map the stores state and dispatch to the props of a component :

    这正是connect所做的。考虑这段代码,实用connect去映射stores状态和发送给组件的属性:

    新葡亰496net 9

    connect

    mapStateToProps and mapDispatchToProps are both pure functions that are provided the stores “state” and “dispatch” respectively. Furthermore, both functions have to return an object, whose keys will then be passed on as the props of the component they are connected to.

    mapStateToProps和mapDispatchToProps均是纯函数,分别为stores提供state和dispatch。此外,这两函数必须返回一个对象,对象的键将被当作它们所连接组件的属性来传递。

    In this case, mapStateToProps returns an object with only one key : “todo”, and mapDispatchToProps returns an object with the destroyTodo key.

    在这种情况下,mapStateToProps 返回一个只有“todo”键的对象,mapDispatchToProps 返回一个带有destroyTodo键的对象。

    The connected component (which is exported) provides todo and destroyTodo as props to TodoItem.

    所被连接的组件(那个被输出的“TodoItem”)把 todo 和 destroyTodo 作为属性提供给TodoItem。

    新葡亰496net 10

    connect within provider

    It’s important to note that only components within the Provider can be connected (In the above diagram, the connect is done through the Provider).

    请注意只有Provider中的组件能够被connected。

    Redux is a powerful tool and even more so when combined with React. It really helps to know why each part of the react-redux library is used, and hopefully after reading this post, the function of Provider and connect is clear.

    Redux是一个强大的工具,甚至与React结合也是如此。 这真的有助于了解react-redux库每个部分的使用,并且希望在阅读这篇文章后,你对Provider和connect的认识更清晰。

    原文:

    5、React第三方组件5(状态管理之Redux的使用⑤异步操作)---2018.03.26

    单一store和多store

    store是应用管理数据的地方,在Redux应用中,我们总是将所有共享的应用数据集中在一个大的store中,而Mobx则通常按模块将应用状态划分,在多个独立的store中管理。

    转变到Redux

    需要添加Redux, React Redux 和Redux Thunk作为依赖项.

     npm install redux react-redux redux-thunk --save
    

    *七、React-Redux的用法

    为了方便使用,Redux的作者封装了一个React专用的库React-Redux本文主要介绍它

    这个库可以选用的。实际项目中,你应该权衡一下,是直接使用Redux,还是使用React-Redux。后者虽然提供了便利,但是需要掌握额外的Api,并且要遵循它的组件拆分规范。

    新葡亰496net 11React & Redux

    React-Redux将所有的组件分为两大类:UI组件(presentational component)和容器组件(container component)。

    UI组件有一下几个特征。

    1)只负责UI的呈现,不带有任何业务逻辑。

    2)没有状态(即不使用this.state这个变量)

    3)所有数据都由参数(this.props)提供

    4)不使用任何Redux的API

    下面就是一个UI组件的例子

    const Title =

    value = > <h1> { value} </h1>;

    因为不含有状态,UI组件又称为“纯组件”,即它纯函数一样,纯粹由参数决定他的值

    容器组件的特征恰恰相反。

    1)负责管理数据和业务逻辑,不负责Ui的呈现

    2)带有内部状态

    3)使用Redux的ApI

    总之,只要记住一句话就可以了:UI组件负责Ui的呈现,容器组件负责管理数据和逻辑

    你可能会问,如果一个组件既有UI又有业务逻辑,怎么办? 答案是,将它拆分成下面的结构:外面是一个容器组件,里面包含了一个UI组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图。

    React-Redux提供connect方法,用于从UI组件生成容器组件。connect的意思,就是将这两种组件连起来。

    connect方法的完整API如下。

    import { connect } from 'react-redux';

    const VIsibleTodoList = connect(

    mapStateToProps,

    新葡亰496net,mapDisPatchToProps

    )

    上面代码中,TodoList是UI组件,VisibleTodoList就是由React-Redux通过connect方法自动生成的容器组件。connect方法接受两个参数:mapStateToPropsmapDispatchToProps。他们定义了UI组件的业务逻辑,前者负责输入逻辑,即将state映射到UI组件的参数,后者负责输出逻辑,即将用户对UI组件的操作映射成Action。

    mapStateToProps是一个函数,它的作用就像他的名字一样,建立一个从state对象到props对象的映射关系。

    作为函数,mapStateProps执行后应该返回一个对象,里面每个键值对就是一个映射,请看下例

    const mapStateToProps = =>{

    return {

    todos:getVisibleTodos(state.todos,state.visibilityFilter)

    }

    }

    上面代码中,mapStateToProps是一个函数,他接受state作为参数,返回一个对象。这个对象有一个todos属性,代表UI组件的同名参数,后面的getVIsibleTodos也是一个函数,可以从state算出todos的值。

    下面就是getVisibleTodos的一个例子,用来算出Todos。

    const getVisibleTodos =(todos , filter) =>{

    switch {

    case 'SHOW_ALL';

    return todos

    case 'SHOW_COMPLETED';

    return todos.filter( t => t.completed)

    case 'SHOW_ACTIVE' ;

    return todos.filter ( t=> ! t.completed)

    default:

    新葡亰496net:境况管理之Redux的利用⑤异步操作。throw new Error ( ' Unknown filter: ' filter)

    }

    }

    mapStateToProps会订阅Store,每当state更新的时候,就会自动执行,重新计算UI组件的参数,从而触发UI组件的重新渲染,

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

    // 容器组件的代码

    // <FilterLink filter = "SHOW_ALL">

    // ALL

    // </FilterLink>

    const mapStateToProps = (state, ownProps) =>{

    return{

    active : ownProps.filter===state.visibilityFilter

    }

    }

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

    connect方法可以省略mapStateToProps参数,那样的话,UI组件就不会订阅Store,就是说Store的更新不会引起UI组件的更新。

    mapDispatchToProps是connect函数的第二个参数,用来建立UI组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当做Action,传给Store,他可以是一个函数,也可以是一个对象。

    如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。

    const mapDispatchToProps = {

    dispatch,

    ownprops

    } = > {

    return {

    onClick: () =>{

    dispatch({

    type: 'SET_VISIBILITY_FILTER',

    filter : ownProps.filter

    });

    }

    };

    }

    从上面代码可以看出,mapDispatchToProps作为函数,应该返回一个对象,该对象的每一个键值对都是一个映射,定义了UI组件的参数怎么发出Action

    如果mapDispatchToProps是一个对象,它的每个键名也是对应的UI组件的同名参数,键值应该是一个函数,会被当做Action Creator,返回的Action会由Redux自动发出。举例来说,上面的mapDispatchToProps写成对象就是下面这样的

    const mapDispatchToProps = {

    onClick : => {

    type: 'SET_VISIBILITY_FILTER',

    filter : filter

    };

    }

    connect 方法生成容器组件后,需要让组件拿到state新葡亰496net:境况管理之Redux的利用⑤异步操作。对象,才能生成UI组件的参数。React-Redux提供的Provider组件,可以让容器组件拿到state

    import { Provider } from 'react-redux'

    import { createStore } from 'redux'

    import todoApp from './reducers'

    import App form ' ./components/App'

    let store = createStore;

    render(

    <Provider store={store}>

    <App/>

    </Provider>

    document.getElementById

    )

    上面代码中,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state啦。

    使用React-Route的项目,与其他项目没有不同之处,也是使用Provider在Router外面包了一层,毕竟Provider的唯一功能就是传入store对象。

    const Root =( { store } ) => (

    <Provider store ={store}>

    <Router>

    <Route path="/" component= { App } />

    </Router>

    </Provider>

    );

    后记:本文属于笔记,所有内容出自是云随风的博客,谢谢大神

    6、React第三方组件5(状态管理之Redux的使用⑥Redux DevTools)---2018.03.27

    JavaScript对象和可观察对象

    Redux默认以JavaScript原生对象形式存储数据,而Mobx使用可观察对象:

    1. Redux需要手动追踪所有状态对象的变更;
    2. Mobx中可以监听可观察对象,当其变更时将自动触发监听;

    理解Redux

    有几个Redux的核心原理,我们必须要理解(译注:话很简单,但是要在大脑里构建出Redux的工作流程是有花很多时间的和精力的).

    1. Redux中有一个全局state对象来管理整个应用的state.在篇文章中,全局对象就是我们组件的初始化对象.
    2. 唯一能改变state的是触发一个action,action是一个描述state应该怎么改变的对象,Action Creators可以被dispatched的函数,触发一个变化,执行的内容是返回一个action
    3. 当一个action被dispatch以后,Reducer执行根据action的内容实际改变state对象,如果action没有找到匹配项,就会返回默认的state.
    4. Reducers是纯函数,他们不能有任何的异步操作和mutate-必须要返回一个修改的copy.
    5. 单个的Reducer可以被combine为一个单一的rootReducer,从而创建一个离散的state聚合体.
    6. Store是把action和reducer组织到一起的工具,他包含了rootReducer代表的状态,中间件,允许你执行实际的dispatchactions
    7. 为了在React中使用Redux,<Provider />组件包装整个应用,传递store到子代组件们.

    开发环境:Windows 8,node v8.9.1,npm 5.5.1,WebStorm 2017.2.2

    不可变(Immutable)和可变(Mutable)

    Redux状态对象通常是不可变的(Immutable):

    switch (action.type) { case REQUEST_POST: return Object.assign({}, state, { post: action.payload.post }); default: retur nstate; }

    1
    2
    3
    4
    5
    6
    7
    8
    switch (action.type) {
      case REQUEST_POST:
        return Object.assign({}, state, {
          post: action.payload.post
        });
      default:
        retur nstate;
    }

    我们不能直接操作状态对象,而总是在原来状态对象基础上返回一个新的状态对象,这样就能很方便的返回应用上一状态;而Mobx中可以直接使用新值更新状态对象。

    设计我们的state

    从我们现在已有的代码里,可以知道我们的state需要3个属性(properties):items,hasErrored,isLoading,这个三个属性相应的需要三个独立的actions.

    现在,这里讲讲为什么Action Creator和Action是不同的,他们也不是1:1的关系:我们需要第四个actiong creator来根据fetch data的不同状态调用其他三个action(creators).这第四个action creator几乎和我们原先的fetchData()一样,但是它不会直接的使用this.setState({isLoading:true})来设置状态,我们将dispatch一个action去做同样的事情:dispatch(isLoading(true)).

    这里我们需要用到 redux-thunk

    mobx-react和react-redux

    使用Redux和React应用连接时,需要使用react-redux提供的Providerconnect

    1. Provider:负责将Store注入React应用;
    2. connect:负责将store state注入容器组件,并选择特定状态作为容器组件props传递;

    对于Mobx而言,同样需要两个步骤:

    1. Provider:使用mobx-react提供的Provider将所有stores注入应用;
    2. 使用inject将特定store注入某组件,store可以传递状态或action;然后使用observer保证组件能响应store中的可观察对象(observable)变更,即store更新,组件视图响应式更新。

    创建actions

    在actions目录下创建itmes.js文件,其中包含我们的action creators.创建三个简单的actions.

        export function itemsHasErrored(bool) {
       return {
           type: 'ITEMS_HAS_ERRORED',
           hasErrored: bool
       };
    }
    export function itemsIsLoading(bool) {
       return {
           type: 'ITEMS_IS_LOADING',
           isLoading: bool
       };
    }
    export function itemsFetchDataSuccess(items) {
       return {
           type: 'ITEMS_FETCH_DATA_SUCCESS',
           items
       };
    }
    
    
        ```
    
    
    
    
    
    
    
    
    
    
    
    如前面提到的,action creators似能返回函数的函数,使用`export`输出单个action creators,便于在代码中使用.
       第二个action creators接受一个布尔值(true/false)最为参数,返回一个有意义的`type`和布尔值,分配合适的属性.
       第三个,`itemsFetchSuccess()`,当数据成功返回以后,传递数据作为`items`属性的值.通过ES6的魔术属性缩写,我们能够返回一个对象含有属性名叫做`items`,他的值是`items`的数组.
    
      (Note: that the value you use for type and the name of the other property that is returned is important, because you will re-use them in your reducers)这一句不知道怎么翻译.
      现在,我们有了三个actions,代表我们的状态,把原来的组件方法`fetchData`该给`itemFetchDaga()`action creator.
      默认情况下,Redux action creators是不支持异步actions的,像是fetching data的操作,所以这里我们使用Redux Thunk.Thunk允许你在action creator里返回一个函数代替实际的action.内部函数接受`dispatch`和`getState`作为参数,但是我们仅仅使用`dispatch`.
      实际的简单例子中五秒以后将会触发`itemHasErrored()`函数.
    
    
    export function errorAfterFiveSeconds() {
    // We return a function instead of an action object
    //dispatch作为参数传递给胖箭头函数
    return (dispatch) => {
        setTimeout(() => {
            // This function is able to dispatch other action creators
            dispatch(itemsHasErrored(true));
        }, 5000);
    };
    

    }

    现在我们知道thunk是什么了.编写`itemsFetchData()`.
    
    export function itemsFetchData(url) {
    return (dispatch) => {
        //已进入fetchdata,按顺序把isLoading state 由
        // false=>true
        dispatch(itemsIsLoading(true));
        //fetch执行实际的异步远程获取数据操作
        fetch(url) 
            .then((response) => {
                if (!response.ok) {//根据状态抛出错误
                    throw Error(response.statusText);
                }
                //isLoading又改为false,加载Loading组件
                dispatch(itemsIsLoading(false));
                return response;
            })
            .then((response) => response.json())
            .then((items) => dispatch(itemsFetchDataSuccess(items)))
            .catch(() => 
            dispatch(itemsHasErrored(true)));
            //捕获错误以后HasError的状态 false=>true
    };
    

    }

    ## 创建我们的reducers
    
    action定义好了以后,可以编写reducers接受actions,接着返回appliction的新状态(译注:实际上store中返回的对象都是一个新的对象,不是原对象的引用,这个就叫做immutable,facebook定义了一个immutable.js的技术实际是也是返回一个新的对象的硬拷贝,但是在原对象和修改对象之间共享了一部分内容,这一点有点微妙).
    注意:在Redux中,所有的reducers不考虑action,都会调用,所以即就是没有action被应用,你也必须要返回一个原来的定义的`state`.
    
    每一个reducer接收两个参数,之前的state和一个`action`对象.也可以使用ES6的属性来调用默认的参数设定到默认的`初始化state`.
    
    在每个reducer内部,使用`switch`申明来决定到底哪个`action.type`相匹配.如果是简单的reducer,可能没有必要使用`switch`,理论上使用`if/else`可能更快一点.
    
    如果`action.type`一点匹配,然后会返回和`action`相关的属性.和前面提到的一样,`type`和`action[属性名]`是在action creators里定义的.
    
    好啦,了解到这些内容,在`reducers/item.js`中创建items reducers
    

    export function itemsHasErrored(state = false, action) {
    switch (action.type) {
    case 'ITEMS_HAS_ERRORED':
    return action.hasErrored;
    default:
    return state;
    }
    }
    export function itemsIsLoading(state = false, action) {
    switch (action.type) {
    case 'ITEMS_IS_LOADING':
    return action.isLoading;
    default:
    return state;
    }
    }
    export function items(state = [], action) {
    switch (action.type) {
    case 'ITEMS_FETCH_DATA_SUCCESS':
    return action.items;
    default:
    return state;
    }
    }

    注意reducer根据结果store的state属性来命名,`action.type`没有必要想对应.前两个表达完整的意思,第三个`items()`就稍微有点不同.
    
    这是因为,可能会有很多条件返回`items`数组:有可能返回所有的数组,有可能在删除dispatch以后返回`items`的次级结构.或者所有的items都被删除了,会返回一个空数组.
    
    为了重新遍历,每一个reducer都会返回一个截然不同的state属性,不需要考虑reducer内部的条件到底有多少.刚开始花了我很长时间想明白这个问题.
    单个的reducers创建好了以后,我们需要把单个的reducer合并(combine)成一个`rootReducer`,创建单一对象.
    
    创建文件`reducers/index.js`
    

    import { combineReducers } from 'redux';
    import { items, itemsHasErrored, itemsIsLoading } from './items';
    //由于每个reducer返回的都是一个对象
    //所以这里的操作就是合并对象的操作,在underscore和loadsh
    //里面可以找到合并js对象的代码
    export default combineReducers({
    items,
    itemsHasErrored,
    itemsIsLoading
    });

    我们从`items`里导入每个reducers,使用redux的`combineReducers()`函数来合并输出单一对象(译注:所以每一个reducer返回的对象的属性名应该是唯一的,否则就覆盖了,前面的内容表达过这个意思)
    因为我们的reducer的名字和在store中使用的属性名一样,所以我们可以使用ES6的对象字面量.
    
    注意,我有意提到了reducer的前缀,所以当我们的application变得比较复杂的时候,不能出现全局性的`hasErrored`和`isLoading`属性.可以使用不同的error和loading state,所以前缀可以给你很大的灵活性.例如
    
    import { combineReducers } from 'redux';
    

    import { items, itemsHasErrored, itemsIsLoading } from './items';
    import { posts, postsHasErrored, postsIsLoading } from './posts';
    export default combineReducers({
    items,
    itemsHasErrored,
    itemsIsLoading,
    posts,
    postsHasErrored,
    postsIsLoading
    });

    替代方法是,可以在import的时候使用别名.但是我更愿意使用独一无二的名字.
    
    ## 配置store,注入到你的app中
    
    操作很直接,创建`store/configureStore.js`
    

    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from '../reducers';
    export default function configureStore(initialState) {
    return createStore(
    rootReducer,
    initialState,
    applyMiddleware(thunk)
    );
    }

    现在在index.js中包含`<Provider />`组件,`configureStore`,配置`store`.包装app(`<ItemList />`),传递进去`store`和`props`.
    

    import React from 'react';
    import { render } from 'react-dom';
    import { Provider } from 'react-redux';
    import configureStore from './store/configureStore';
    import ItemList from './components/ItemList';
    const store = configureStore(); // You can also pass in an initialState here
    render(
    <Provider store={store}>
    <ItemList />
    </Provider>,
    document.getElementById('app')
    );

    我知道,其实花了很多努力才到了这一步,但是随着设置的完成,我们就可以使用配置来操纵我们的组件了(译注:这里是意译,组件又称为木偶组件,意思很清楚吧?谁是拿着吊线的人呢?就是redux).
    
    ## 把组件转化为使用Redux store和方法
    
    跳回到`components/ItemList.js`
    
    在顶部导入需要的部分
    

    import { connect } from 'react-redux';
    import { itemsFetchData } from '../actions/items';

    `connect`可以让组件链接到Redux的store,`itemsFetchData`是在开始写的action creator.我们仅仅需要导入actin creator.使用`dispatch`来触发actions.(译注:redux里面有很多内容其实是很基础的,例如这里,javascript的函数是一类对象,在js中函数是传引用的,所以函数名可以作为函数的引用,通过另一函数的参数来传递. 厉害 
    		

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net:境况管理之Redux的利用⑤异步操作

    关键词:

上一篇:学习总结,React技术栈耕耘

下一篇:没有了