# React 状态管理

由于应用需要管理更多的状态,如何让状态更清晰,可预测,是我们在项目中需要考虑的,由此 React 引入了 Redux源码study (opens new window)

# Redux

# 三大原则

  • 单一数据源
  • State 是只读的
  • 使用纯函数来执行修改

# 基础

# 数据流

严格的单向数据流是 Redux 架构的设计核心,所有数据的生命周期都有4个步骤

  • 调用 store.dispatch(action) ;action 是描述事件发生情况的对象
  • Redux store 调用传入的 reducer 函数,当前 reducer 传入 stateaction 两个参数
    • reducer 应该是个纯函数,完全可预测
    • API 调用或路由跳转,应该在  disaptch action 前
  • 根 reducer 应该合并多个子 reducer 形成一个单一的 state 树
    • 使用 combineReducers() 函数合并
  • Redux store 保存了根 reducer 返回完整的树
    • 手动更新订阅 store.subscribe(listener)
    • store.getState() 获取当前最新 state

# Action

Action 是数据从应用传到 store 的有效载荷

  • store 数据唯一的来源
  • 通过调用 store.dispatch() 将 action 传到 store

# Reducer

指定应用状态的变化如何响应 actions 并发送到 store

  • 是一个纯函数
  • 接受旧的 stateaction,返回新的 state
  • 只描述有事情发生,并不描述应用如何更新 state

# Store

一个维持所有 state 树的对象,改变 store 中 state 唯一的方法是对它 dispatch 一个 action

  • Redux 应用只有一个单一的 store,需要拆分数据处理逻辑时,使用 reducer 组合
  • getState 获取 state
  • dispatch(action) 更新 state
  • subscribe(Function) 变化监听器

# 简单的示例

  • 创建 book 模块 reducer
import actionTypes from "./../actions/actionTypes";
const initState = [] // 初始状态
export default (state = initState, action) => {
  switch (action.type) {
    case actionTypes.INSERT:
      return state.map(v => {
        if (v.id === action.payload.id) {
          v.star++
        }
        return v
      });
    default:
      return state;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 合并所有子 reducer 模块
// store/recuders/index.js
import { combineReducers } from 'redux'
import books from './books'

// 合并redux
export default combineReducers({
  books
})
1
2
3
4
5
6
7
8
  • 创建 store
// store/index.js
import { createStore } from 'redux'
import rootReducer from './reducers'

export default createStore(rootReducer)
1
2
3
4
5
  • 创建描述 action
// actionTypes.js
export default {
  INSERT: 'INSERT'
}

// action/books.js
import actionTypes from "./actionTypes";
// 优先使用函数
export const insert = (id) => ({
  type: actionTypes.INSERT,
  payload: {
    id
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 更新视图
// views/Redux.js
import React, { useState } from 'react'
import { insert } from './../store/actions/books'

/**
 * 触发一个action,所有的redux都会收到
 */

const ReduxPage = props => {
    console.log(props)
    const state = props.getState().books
    const [books, setBooks] = useState(state)
    const add = (id) => {
        props.dispatch(insert(id))
        setBooks(props.getState().books)
    }
    return (
      <div>
        <table border="1" cellSpacing="0">
          ...
          <tbody>
            {books.map(book => {
              return (
                <tr key={book.id}>
                  <td>{book.id}</td>
                  <td>{book.title}</td>
                  <td>{book.author}</td>
                  <td>
                    {book.star}
                    <button onClick={add.bind(this, book.id)}>+</button>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    );
};

export default ReduxPage
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
30
31
32
33
34
35
36
37
38
39
40
41

# react-redux

Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、jQuery 甚至纯 JavaScript。react-redux 则是搭配 react 状态管理利器。

# Provider

通过 <Provider>高阶组件来让所有容器组件都可以访问 store,而不必显示地传递它。只需要在渲染根组件时使用即可

import { Provider } from 'react-redux'

import App from './App'
import store from "./store";

ReactDOM.render(
  <Provider store={store}>
    <HashRouter>
      <Route render={() => <App /> }/>
    </HashRouter>
  </Provider>,
  document.getElementById("root")
);
1
2
3
4
5
6
7
8
9
10
11
12
13

# connect

通过 connect()() 方法来链接组件,这个方法做了性能优化来避免很多不必要的重复渲染

  • 可以使用 mapStateToProps 重新定义数据挂在到当前组件 props 上
  • 可以使用 mapDispatchToProps 重新定义方法注入到当前组件 props 上
import React, { useState } from 'react'
import { connect } from 'react-redux'
import { insert } from './../store/actions/books'

const ReduxPage = props => {
    console.log(props)
    const [books] = useState(props.books)
     return (
      <div>
        <table border="1" cellSpacing="0">
          ...
          <tbody>
            {books.map(book => {
              return (
                <tr key={book.id}>
                  ...
                  <td>
                    {book.star}
                    <button onClick={props.add.bind(this, book.id)}>+</button>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    )
}

// 重新定义数据挂在到当前组件props上
const mapStateToProps = state => {
  console.log(state)
  return {
    books: state.books
  }
}

// 重新定义方法注入到当前组件props上 
const mapDispatchToProps = dispatch => {
  return {
    add: id => dispatch(insert(id))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ReduxPage)
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
更新于: 6/12/2020, 9:52:27 AM