oakland / tecblog

My tech blogs
4 stars 0 forks source link

我对 redux 的理解 #46

Open oakland opened 7 years ago

oakland commented 7 years ago

为什么要用 redux

https://blog.logrocket.com/why-use-redux-reasons-with-clear-examples-d21bffd5835/

实际上 redux 只是简单的状态管理工具,但是在 react 中用 redux 的根本原因不是因为 component 的 state 的管理混乱,而是因为项目扩大、复杂化之后,组件之间通讯的复杂度提高了,所以要引入 redux 来对组件之间的通讯做管理。 所以说在 react 中引入 redux 的根本原因是为了方便管理组件之间的通信。因此我之前单纯的把 redux 管理好的动作,传递给子组件,然后通过子组件再传递给子组件的这种方式,其实并不是真正对 redux 的应用,还是按照原来的方式来进行管理的。失去了用 redux 的意义。

With Redux, the state of your application is kept in a store, and each component can access any state that it needs from this store. State management is essentially a way to facilitate communication and sharing of data across components. It creates a tangible data structure to represent the state of your app that you can read from and write to. That way, you can see otherwise invisible states while you’re working with them. Most libraries, such as React, Angular, etc. are built with a way for components to internally manage their state without any need for an external library or tool. It does well for applications with few components, but as the application grows bigger, managing states shared across components becomes a chore.

再来

最近又再看 react 相关的文章,必然少不了 react-router 和 redux。这里对 redux 再次做一个总结。其实现在就会去看源码,源码写的很清楚了,当时非常不理解,完全就是去看别人写的文字,自然理解起来比较慢。现在看源码,一看一个准,一下就明白 redux 的各个函数都是在干什么了,其实写的还是挺清晰的,而且内容不多,调用关系也不复杂。 dispatch 其实就是和 setState 是类似的,必须显式的声明一个动作来表达要做什么。然后执行这个动作就是直接返回一个新的 state,而不是去修改现有的 state。dispatch 结束之后就依次调用 listener 把消息发布出去。 直接放源码:

  function dispatch(action) {
    // ... 省略了参数校验过程 ...
    try {
      isDispatching = true;
      currentState = currentReducer(currentState, action);
    } finally {
      isDispatching = false;
    }

    // 订阅上的 listener 在这里发布,在 listener 中需要通过 store.getState() 来获取新的 state
    var listeners = currentListeners = nextListeners;
    for (var i = 0; i < listeners.length; i++) {
      var listener = listeners[i];
      listener();
    }

    return action;
  }

官网的例子也是一个最基本的实现,非常清晰明了。https://redux.js.org/introduction/getting-started

import { createStore } from 'redux'

/**
 * This is a reducer - a function that takes a current state value and an
 * action object describing "what happened", and returns a new state value.
 * A reducer's function signature is: (state, action) => newState
 *
 * The Redux state should contain only plain JS objects, arrays, and primitives.
 * The root state value is usually an object.  It's important that you should
 * not mutate the state object, but return a new object if the state changes.
 *
 * You can use any conditional logic you want in a reducer. In this example,
 * we use a switch statement, but it's not required.
 */
function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counterReducer)

// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// There may be additional use cases where it's helpful to subscribe as well.

store.subscribe(() => console.log(store.getState()))

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}

可以看到 reducer 其实就是一个大的 switch...case...,然后 action 就是一个带着 type 的 object。

immutability

在 redux 中再次见到了这个概念,而且官网也给出了例子和一些可以参考的文章,值得再看下这个 immutability 的概念。

reducer

官网给出的概念最清楚了 Reducers A reducer is a function that receives the current state and an action object, decides how to update the state if necessary, and returns the new state: (state, action) => newState. You can think of a reducer as an event listener which handles events based on the received action (event) type.

thunk

redux 还牵扯到 thunk 的概念,这个在 sicp 中也讲到了。

一篇很好的复盘 redux 的文章

完全理解 redux(从零实现一个 redux) 上面这篇文章对于理解 redux 简直就是完美的复盘,非常有帮助。

我们需要约束,不允许计划外的 count 修改,我们只允许 count 自增和自减两种改变方式!

我们对 state 的可以修改的可能性必须要做出限制,不应该让人随便修改。

前言

最近看了很多关于 react, redux 相关的文章,尤其是关于 redux 相关的文章。 redux 中的概念很多,比如 action & bindActionCreators, reducer & combineReducers, provider, store & createStore & applyMiddleware, mapStateToProps, mapDispatchToProps, connect, etc. 虽然对于 redux 的这些概念已经了解了,对于整个 app 的运转流程也了解了,但是还是觉得没有从一个高度和深度上去理解 redux 架构。

redux 的可预见性(predictable)

知乎上有个问题,该如何通俗易懂的理解 redux,这个问答下面有很多很好的回答以及很棒的链接。下面就是其中之一: https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6 这篇文章中有一句话:

Just like Flux, it makes state changes in apps more predictable.

可见,其实之所以在 react 上面增加一个层次,一个架构的根本目的就是让你的 状态 更加具有可预见性。就是让你的应用更容易管理。尤其是 action 作为一个描述性的内容,能让人更容易理解状态的变化内容。从而实现可预见性( predictable )。

store 和 reducer 之间的关系

https://medium.com/front-end-developers/the-command-pattern-c51292e22ea7 这篇文章将 redux 和设计模式中的 命令模式(command patter) 做了类比。我觉得对于理解 redux 也很有帮助。

The Store is instantiated with “reducers”, descriptions on how the Store change.

作者认为 Store 其实就是 reducers 实例化之后的内容,也就是说 Store 其实是由 reducers 生成的,而 reducers 的作用就是告诉人们如何改变 Store。这样就很容易理解 reducers 和 Store 之间的关系了。而且实际上 Store 生成的时候也是需要传入 reducers 的。

const store = createStore(reducers);

就是通过这种方式和 reducers 纠缠在一起的。

redux 借鉴的概念

https://www.ibm.com/developerworks/library/wa-manage-state-with-redux-p1-david-geary/ 这是一个系列文章,讲的也不错。虽然我对传统的软件工程不是很理解,但是总感觉 react 的很多东西应该是从后端或者软件工程中借鉴而来的。比如 flux 和传统 MVC 之间应该会很相似。然后这篇文章中作者说 redux 的 reducer 概念是从 Elm 这个语言中借鉴而来的。

redux 和 react 之间的关系

redux 是 data,react 是 view。这个是 udemy 中 react + redux 课程 lecture 36 第 00:26 的说的。

reducers

A reducer is a functions that returns a piece of application state.( udemy, react + redux, lecture 39, 00:38 ) 这里强调每个 reducer 只返回整个 app 的 state 中的一部分。所以每个 reducer 只返回一部分 state。

Container

A container is just a component that has direct access to the state that is produced by redux.

udemy 中 react + redux 课程 lecture 41 第 00:09 的说的。

有了 redux 之后,任何一个组件都可以变成一个 container,就是说任何一个组件都可以直接从 store 中获得自己想要的 state,所以最外层的 App 组件就不需要再传入 props 给它的子组件了。而如果没有 redux 的话,App 组件必然要定义自己的 state,然后再把 state 中的很多字段作为 props 传给子组件,一层一层传递下去,显得整个嵌套关系很麻烦,因为你需要一层一层去找真正的 props 是从哪里传递下来的。而 redux 解决了这个问题,你随时可以把一个组件改写成 container,不需要担心是否从父组件上有什么内容传递,因为你直接是从 store 中获得的 state 字段。

Reactjs小书 和 阮一峰的 redux 系列文章

阮一峰关于 redux 有一个系列文章,总共三篇: redux基本用法 中间件与异步操作 React-Redux 用法 这三篇文章对于 redux 讲解的很好,Reactjs 小书的内容和第一篇文章很像,尤其是在实现 createStore 的方法上面,一步步告诉大家如何去实现 createStore,而不是像阮一峰的文章直接告诉你。非常值得一看。

纯函数

纯函数的概念,Reactjs 小书里面讲的非常清楚。值得一看。直白一点讲,纯函数就是既不依赖函数本身参数以外的参数,也不会改变参数本身。这个概念是 reducer 的原型。 http://huziketang.com/books/react/lesson32

xiaoshen 推荐我看的几篇文章

网络上的几篇文章

understanding-redux-the-worlds-easiest-guide-to-beginning-redux

oakland commented 3 years ago

最近又开始看 redux 了,又有了一些更加明确和深刻的理解

https://juejin.cn/post/6844904036013965325#heading-7

https://github.com/brickspert/blog/issues/22

上面这两篇文章都不错,认真看看。

然后关于 react 如何 redux 关联起来的,自然用的就是 react-redux,在这篇文章 中有说明,实际上我一直好奇 subscribe 到底在 react-redux 中是如何使用的。里面只是说用了 forceUpdate 的方式。将信将疑的我后来在官网中找打了说明:

Each component that sees its data has changed forces a re-render with the new data, so it can update what's shown on the screen

看了 v5.x 的源码,确实用的是 forceUpdate 的方式,但是后面 v6 和 v7 的版本却没有发现 forceUpdate 的身影,而且改用了 hooks 的写法,没有仔细探究了,所以还是没有明确如何 forceUpdate 的。

redux 和 immutability

https://daveceddia.com/react-redux-immutability-guide/#how-to-update-state-in-redux

From there, you can use the produce function to build yourself a nice little mutable playground, where all your mutations will be handled by the magic of JS Proxies.

这里提到了 immer 和 proxy 是有关系的,回头可以研究一下 immer 和 proxy 的关系。然后再详细看看 xiaoshen 之前分享的关于 immutable 和 immer 的内容,里面的算法。