美文网首页
彻底捋顺redux

彻底捋顺redux

作者: forever_提拉米苏 | 来源:发表于2021-01-25 16:39 被阅读0次

    仔细算来,redux用了也有一年多了,但一直是用的时候捡起来,不用又忘了的情况,处在似懂非懂的阶段。这篇文章用来对redux做一个总结,彻底搞懂redux。


    Redux是JavaScript状态容器,提供可预测化的状态管理。redux提出的目的是为了解决大型的复杂应用中组件之间的通信困难、代码结构混乱等问题。具体来说就是以下几种场景:
    就使用场景来说,

    • 用户的使用方式复杂
    • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
    • 多个用户之间可以协作
    • 与服务器大量交互,或者使用了WebSocket
    • View要从多个来源获取数据

    就组件常见来说,

    • 某个组件的状态,需要共享
    • 某个状态需要在任何地方都可以拿到
    • 一个组件需要改变全局状态
    • 一个组件需要改变另一个组件的状态

    发生上面情况时,如果不使用 Redux 或者其他状态管理工具,不按照一定规律处理状态的读写,代码很快就会变成一团乱麻。你需要一种机制,可以在同一个地方查询状态、改变状态、传播状态的变化。

    开始之前先来了解几个概念

    Redux 由以下组件组成:
    • Action,这是一个用来描述发生了什么事情的对象。
    • Reducer,接收action并更新Store。
    • Store,整个程序的状态/对象树保存在Store中。
    • View,只显示 Store 提供的数据。
    Redux有三大原则
    • 单一数据源store:store是整个应用的数据存储中心(store tree),集中大部分页面需要的状态数据;
    • state是只读的,所以需要view触发action去更新store;
    • 使用纯函数Reducer执行state更新。
    image.png

    下面开始逐一介绍

    1. Store
      Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。Redux 提供createStore这个函数,用来生成 Store。store提供getState方法获取所有的state
    import { createStore } from 'redux';
    const store = createStore(reducer);
    
    const state = store.getState();
    
    1. Action
      上面说过state是只读的,State 的变化会导致 View 的变化。但是用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。
    const action = {
      type: 'ADD',
      payload: 1
    };
    

    Action 是一个对象。其中的type属性是必须的,表示 Action 的名称。Action 描述当前发生的事情。改变 State 的唯一办法,就是使用 Action。它会运送数据到 Store。
    定义好Action了,怎么发出呢?store.dispatch()是 View 发出 Action 的唯一方法。

    import { createStore } from 'redux';
    const store = createStore(reducer);
    
    store.dispatch(action);
    
    1. reducer
      Reducer 是一个纯函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
    // 这里reducer也可以进行拆分,放到单独一个文件中
    import { createStore } from 'redux';
    
    const defaultState = 0;
    const increaseReducer = (state = defaultState, action) => {
      switch (action.type) {
        case 'ADD':
          return state + action.payload;
        default: 
          return state;
      }
    }
    const store = createStore(increaseReducer);
    

    上面代码中,由于store.dispatch方法会触发 Reducer 的自动执行,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。
    createStore接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State。

    Reducer 函数最重要的特征就是它必须是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。纯函数是函数式编程的概念,必须遵守一些约束:

    • 不得改写参数
    • 不能调用系统 I/O 的API
    • 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果

    ps: reducer拆分
    试想一下,在一个大型应用中 State 必然十分庞大,导致 Reducer 函数也十分庞大。在上面的例子中我们提到reducer也可以进行拆分,放在一个文件里面,然后统一引入。Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。

    import { combineReducers } from 'redux'
    import increase from './increase'
    import decrease from './decrease'
    
    const reducer = combineReducers({
      value: increase,
      decrease  // 这种写法有一个前提,就是 State 的属性名必须与子 Reducer 同名。否则就用上面这种写法
    })
    export default reducer;
    
    
    1. React-Redux 的用法
      React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

    UI 组件有以下几个特征:

    • 只负责 UI 的呈现,不带有任何业务逻辑
    • 没有状态(即不使用this.state这个变量)
    • 所有数据都由参数(this.props)提供
    • 不使用任何 Redux 的 API

    容器组件的特征如下:

    • 负责管理数据和业务逻辑,不负责 UI 的呈现
    • 带有内部状态
    • 使用 Redux 的 API
    connect

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

    import { connect } from 'react-redux'
    import mapStateToProps from ''
    import mapDispatchToProps  from ''
    
    const App= connect(
      mapStateToProps,   // value 来自这里
      mapDispatchToProps   // onIncreaseClick 来自这里
    )(Counter)
    
    class Counter extends Component {
      render() {
        const { value, onIncreaseClick } = this.props
        return (
          <div>
            <span>{value}</span>
            <button onClick={onIncreaseClick}>Increase</button>
          </div>
        )
      }
    }
    
    export default App
    

    上面代码中,Counter是 UI 组件,App就是由 React-Redux 通过connect方法自动生成的容器组件。

    mapStateToProps

    mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从state对象(外部的)到props对象(UI 组件的)的映射关系。mapStateToProps执行后返回一个对象,里面的每一个键值对就是一个映射。

    const mapStateToProps = state => {
      return {
        value: state.value
      }
    }
    

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

    mapDispatchToProps

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

    const mapDispatchToProps = dispatch=> {
      return {
        onIncreaseClick: () => {
          dispatch({
            type: 'ADD',
            payload: 1
          });
        }
      };
    }
    
    <Provider> 组件

    connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。如果一级级将state传下去就很麻烦,React-Redux 提供Provider组件,可以让容器组件拿到state。

    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    import reducer from './reducers'
    import App from './app'
    
    let store = createStore(reducer);
    
    render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    )
    

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

    redux可以使代码结构更加规范,代码可读性更强。因为React提出将展示组件/UI组件(业务逻辑)与容器组件(数据源)分离的思想,所以降低了React 与Redux之间的耦合度。


    为什么我能够看得更远,那是因为我站在巨人的肩上,以上内容来源:
    http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html

    相关文章

      网友评论

          本文标题:彻底捋顺redux

          本文链接:https://www.haomeiwen.com/subject/qnjezktx.html