为什么redux要返回一个新的state引发的血案

栏目: IOS · Android · 发布时间: 5年前

内容简介:博主在面试的过程中,面试官问 : “看你简历,Vue和React都使用过,你能说一下Vue和React的区别嘛?”, 然后吧唧吧唧说了一下,于是!血案发生了,当我答道Vuex和Redux的时候,面试官问了一句,为什么Redux总是要返回一个新的过了大半个月,自己总结面试经验的时候,把Redux的源码看了一遍,ojbk,看的晕头转向,然后去github上看了一些大哥们的解读,再自己总结一哈,写个专栏,用于自己以后的复习Redux 是 JavaScript 状态容器,提供可预测化的状态管理方案, 官网里是这么

博主在面试的过程中,面试官问 : “看你简历,Vue和React都使用过,你能说一下Vue和React的区别嘛?”, 然后吧唧吧唧说了一下,于是!血案发生了,当我答道Vuex和Redux的时候,面试官问了一句,为什么Redux总是要返回一个新的 state ?返回旧的 state 为什么不行 ?面试结果不用说,GG了。

重振旗鼓

过了大半个月,自己总结面试经验的时候,把Redux的源码看了一遍,ojbk,看的晕头转向,然后去github上看了一些大哥们的解读,再自己总结一哈,写个专栏,用于自己以后的复习

基础了解

Redux是什么?

Redux 是 JavaScript 状态容器,提供可预测化的状态管理方案, 官网里是这么介绍的 :

// Redux is a predictable state container for JavaScript apps.
复制代码

那么它能做什么?

// It helps you write applications that behave consistently, run in different environments (client, server, and native) and are easy to test.
  // On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
复制代码

三大原则

  • 单一数据源 : 整个应用的 state 都存储在一颗 state tree 中,并且只存在与唯一一个 store 中

  • state 是只读的 : 唯一改变 state 的方法只能通过触发 action ,然后通过 action 的 type 进而分发 dispatch 。不能直接改变应用的状态

  • 状态修改均由纯函数完成 : 为了描述 action 如何改变 state tree,需要编写 reducers

修修边幅

这里我们先来了解一下store、middleware、action、reducer等知识

store

这里的 store 是由 Redux 提供的 createStore(reducers, preloadedState, enhancer) 方法生成。从函数签名看出,要想生成 store,必须要传入 reducers,同时也可以传入第二个可选参数初始化状态(preloadedState)。第三个参数一般为中间件 applyMiddleware(thunkMiddleware) ,看看代码,比较直观

import { createStore, applyMiddleware } from 'redux'
  import thunkMiddleware from 'redux-thunk' // 这里用到了redux-thunk

  const store = createStore(
    reducers,
    state,
    applyMiddleware(thunkMiddleware) // applyMiddleware首先接收thunkMiddleware作为参数,两者组合成为一个新的函数(enhance)
  )
复制代码

redux 中最核心的 API 就是 —— createStore , 通过 createStore 方法创建的store是一个对象,它本身包含4个方法 :

  • getState() : 获取 store 中当前的状态。

  • dispatch(action) : 分发一个 action,并返回这个 action,这是唯一能改变 store 中数据的 方式。

  • subscribe(listener) : 注册一个监听者,它在 store 发生变化时被调用。

  • replaceReducer(nextReducer) : 更新当前 store 里的 reducer,一般只会在开发模式中调用该方法。

middleware

下图中表达的是Redux 中一个简单的同步数据流动场景,点击 button 后,在回调中分发一个 action, reducer 收到 action 后,更新 state 并通知 view 重新渲染。

单向数据流,看着没什么问题。 但是,如果需要打印每一个 action 信息来调试,就得去改 dispatch 或者 reducer 实现,使其具有 打印日志的功能。

又比如,点击 button 后,需要先去服务端请求数据,只有等数据返回后,才能重新渲染 view,此时我们希望 dispatch 或 reducer 拥有异步请求的功能。再比如,需要异步请求数据返回后,打印一条日志,再请求数据,再打印日志,再渲染。

为什么redux要返回一个新的state引发的血案

面对多样的业务场景,单纯地修改 dispatch 或 reducer 的代码显然不具有普适性,Redux 借鉴了 Node.js Koa 里 middleware 的思想,Redux 中 reducer 更关心的是数据的转化逻辑,所以 middleware 就是为了增强 dispatch 而出现的。

为什么redux要返回一个新的state引发的血案

Action

// 引用官网的介绍

  // Actions are payloads of information that send data from your application to your store. 
  // They are the only source of information for the store
  // You send them to the store using store.dispatch().
复制代码

Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。简单来说,Action就是一种消息类型,他告诉Redux是时候该做什么了,并带着相应的数据传到Redux内部。

Action就是一个简单的对象,其中必须要有一个type属性,用来标志动作类型(reducer以此判断要执行的逻辑),其他属性用户可以自定义。如:

const START_FETCH_API = 'START_FETCH_API'
复制代码
{
    type: START_FETCH_API,
    data: {
      id: itemId,
      value: 'I am Value'
    }
  }
复制代码

Action Creator

看看官网中的介绍 : Action Creator are exactly that—functions that create actions. It's easy to conflate the terms “action” and “action creator”, so do your best to use the proper term。 也就是说 : Redux 中的 Action Creator 只是简单的返回一个 Action

function fetchStartRequestApi(jsondata) {
    return {
      type: START_FETCH_API,
      data: jsondata
    }
  }
复制代码

我们知道,Redux 由 Flux 演变而来,在传统的 Flux 中, Action Creators 被调用之后经常会触发一个dispatch。比如:

function fetchStartRequestApiDispatch(jsondata) {
    const action = {
      type: START_FETCH_API,
      data: jsondata
    }
    dispatch(action)
  }
复制代码

但是,在Redux中,我们只需要把 Action Creators 返回的结果传给 dispatch() ,就完成了发起一个dispatch 的过程,甚至于 创建一个 被绑定的 Action Creators 来自动 dispatch

// example 1
  dispatch(fetchStartRequestApi(jsondata))

  // example 2
  const boundFetchStartRequestApiDispatch = jsondata => dispatch(fetchStartRequestApi(jsondata))
复制代码

这里有人就要昏厥了,dispatch() 是个啥?其实前面就讲过了,通过 createStore() 创建的 store 对象,他有一个方法 : dispatch(action),store 里能直接通过 store.dispatch() 调用 dispatch() 方法,但是多数情况下,我们都会使用 react-redux 提供的 connect() 帮助器来调用。bindActionCreators() 可以自动把多个 action 创建函数绑定到 dispatch() 方法上。

Reducers

// 引用官网的介绍

  // Reducers specify how the application's state changes in response to actions sent to the store. 
  // Remember that actions only describe what happened, but don't describe how the application's state changes
复制代码

上边也说过了,Reducers必须是一个纯函数,它根据action处理state的更新,如果没有更新或遇到未知action,则返回旧state;否则返回一个新state对象。__注意:不能修改旧state,必须先拷贝一份state,再进行修改,也可以使用Object.assign函数生成新的state。__具体为什么,我们读源码的时候就知道啦~

永远不要在 reducer 里做这些操作:

  • 修改传入参数;

  • 执行有副作用的操作,如 API 请求和路由跳转;

  • 调用非纯函数,如 Date.now() 或 Math.random();

下边上个例子代码,帮助消化,发送请求,获取音乐列表

// action.js

  /*
   * action 类型
  */  
  export const START_FETCH_API = 'START_FETCH_API'
  export const STOP_FETCH_API = 'STOP_FETCH_API'
  export const RECEIVE_DATA_LIST = 'RECEIVE_DATA_LIST'
  export const SET_OTHER_FILTERS = 'SET_OTHER_FILTERS'

  /*
   * 其它的常量
  */
  export const otherFilters = {
    SHOW_ALL: 'SHOW_ALL',
    SHOW_ACTIVE: 'SHOW_ACTIVE'
  }

  /*
   * action 创建函数
  */
  export function startFetchApi() {
    return {
      type: START_FETCH_API
    }
  }

  export function stopFetchApi() {
    return {
      type: STOP_FETCH_API
    }
  }

  export function receiveApi(jsondata) {
    return {
      type: RECEIVE_DATA_LIST,
      data: jsondata
    }
  }

  export function setOtherFilters (filter) {
    return {
      type: SET_OTHER_FILTERS,
      data: filter
    }
  }

  // 异步
  export const fetchMusicListApi = (music_id) => dispatch => {
    dispatch(startFetchApi())
    fetch({
      url: url,
      method: 'POST',
      data: {
        music_id: music_id
      }
    }).then((res) => {
      dispatch(stopFetchApi())
      dispatch(receiveApi())
    }).catch((err) => {
      console.log(err)
    })
  }
复制代码
// reducers.js
  
  // 引入 action.js
  import { otherFilters } from './action'

  // 初始 state
  const initialState = {
    otherFilters: otherFilters.SHOW_ALL,
    list: [],
    isFetching: false
  }

  function reducers(state, action) {
    switch(action.type) {
      case SET_OTHER_FILTERS: 
        return Object.assign({}, state, {
          otherFilters: action.payload.data
        })
      case START_FETCH_API:
        return Object.assign({}, state, {
          isFetching: true
        })
      case STOP_FETCH_API:
        return Object.assign({}, state, {
          isFetching: false
        })
      case RECEIVE_DATA_LIST:
        return Object.assign({}, state, {
          list: [ ...action.payload.data ]
        })
      default: 
        return state
    }
  }
复制代码

注意

  1. 不要修改 state。 使用 Object.assign() 新建了一个副本。不能这样使用 Object.assign(state, { otherFilters: action.payload.data }),因为它会改变第一个参数的值。你必须把第一个参数设置为空对象

  2. 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state。

心中有疑惑

  • bindActionCreators() 是如何自动帮我把action绑定到dispatch上的?

  • 什么是纯函数?

  • 为什么reducer必须是纯函数?

  • 为什么只能通过action来修改state,直接修改有什么问题?

  • bindActionCreators() 是如何自动帮我把action绑定到dispatch上的?

  • 为什么 reducers 在 default 的情况下,一定要返回旧的state?

  • ...

相关链接


以上所述就是小编给大家介绍的《为什么redux要返回一个新的state引发的血案》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深度学习

深度学习

[美] 伊恩·古德费洛、[加] 约书亚·本吉奥、[加] 亚伦·库维尔 / 赵申剑、黎彧君、符天凡、李凯 / 人民邮电出版社 / 2017-7-1 / 168

《深度学习》由全球知名的三位专家Ian Goodfellow、Yoshua Bengio 和Aaron Courville撰写,是深度学习领域奠基性的经典教材。全书的内容包括3个部分:第1部分介绍基本的数学工具和机器学习的概念,它们是深度学习的预备知识;第2部分系统深入地讲解现今已成熟的深度学习方法和技术;第3部分讨论某些具有前瞻性的方向和想法,它们被公认为是深度学习未来的研究重点。 《深度......一起来看看 《深度学习》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具