1. redux

1.1 redux使用的场景

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

    1.2 redux的设计思想

    :::success web应用是一个状态机, 视图与状态是一一对应的
    所有的状态, 保存在一个对象里面。 :::

    1.3 基本概念和 API

    1.3.1 store

    Store就是保存数据的地方, 可以把它看成一个容器,整个应用只能有一个Store
    Redux提供createStore这个函数, 用来生成store.
    1. import { createStore, applyMiddleware } from 'redux'
    2. import { composeWithDevTools} from 'redux-devtools-extension'
    3. //引入reduce
    4. import reducer from './reducers'
    5. //引入thunk 用来处理异步方法
    6. import thunk from 'redux-thunk'
    7. //暴露一个store
    8. export default createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));
    createStore函数接收另一个函数作为参数, 返回新生成的Store对象

    1. store.getState()

    1. const state = store.getState();

    2. store.dispatch()

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

    3. store.subscribe()

    Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。 ```javascript import { createStore } from ‘redux’; const store = createStore(reducer);

store.subscribe(listener);

  1. 显然,只要把 **View **的**更新函数**(对于 React 项目,就是组件的render方法或setState方法)放入**listen**,就会实现 **View** 的**自动渲染**。<br />**store.subscribe**方法返回一个**函数**,调用这个函数就可以**解除监听**。
  2. <a name="m2IWo"></a>
  3. ### 1.3.2 state
  4. **Store**对象包含**所有数据**。 如果想得到某个数据, 就要对**Store**生成快照。这种时点的数据集合, 就叫做**State.**<br />当前时刻的**State,**可以通过**store.getState()**拿到。
  5. ```javascript
  6. import { createStore } from 'redux';
  7. const store = createStore(fn);
  8. const state = store.getState();

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

1.3.3 Action

state的变化, 会导致View的变化。但是,用户接触不到State,只能接触到View。所以,State的变化必须是View导致的。 Action就是View触发的通知, 表示State应该要发生变化了。
Action是一个对象。 其中type属性是必须的,是action的名称,描述当前发生的事情data属性是可选的, 表示携带的参数

  1. const action = {
  2. type: 'ADD_TODO',
  3. payload: 'Learn Redux'
  4. };

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

1. AcrionCreator:

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

  1. const ADD_TODO = '添加 TODO';
  2. function addTodo(text) {
  3. return {
  4. type: ADD_TODO,
  5. text
  6. }
  7. }
  8. const action = addTodo('Learn Redux');

上面代码中,addTodo函数就是一个 Action Creator

  • store.dispatch():

store.dispatchview触发Action的唯一方法。

  1. //action creator
  2. import { createStore } from 'redux';
  3. const store = createStore(fn);
  4. store.dispatch({
  5. type: 'ADD_TODO',
  6. payload: 'Learn Redux'
  7. });
  8. //store.dispatch
  9. store.dispatch(addTodo('Learn Redux'));

1.3.4 Reducer

Store收到Actions以后, 必须给出一个新的state, 这样View才会发生变化。这种State的计算过程就叫做Reducer.
Reducer是一个函数, 它接收Action当前State作为参数, 返回一个新的State.

  1. import {INCREMENT,DECREMENT} from '../constants/count'
  2. const initData = 0 //初始化状态
  3. export default function increment(preState = initData, action){
  4. //action对象中包含type和data
  5. const {type, data} = action;
  6. //根据type判断
  7. switch (type) {
  8. case INCREMENT: //+
  9. return preState+data
  10. case DECREMENT: //-
  11. return preState-data
  12. default:
  13. return preState
  14. }
  15. }

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

  1. import { createStore } from 'redux';
  2. const store = createStore(reducer);

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

1. combineReducers:

Reducer 函数负责生成 State。由于整个应用只有一个 State 对象,包含所有数据,对于大型应用来说,这个 State 必然十分庞大,导致 Reducer 函数也十分庞大。
combineReducers方法用于Reducer的拆分。你只需要定义各个子Reducer函数, 然后用这个方法, 将他们合成一个大的Reducer.

  1. import count from './count'
  2. import person from './person'
  3. import { combineReducers } from 'redux'
  4. export default combineReducers({
  5. count,
  6. person
  7. })

总之: combineReducer()做的就是产生一个整体的Reducer函数。该函数根据Statekey去执行相应的子Reducer,并将返回结果合成一个大的State对象
你可以把所有子 Reducer 放在一个文件里面,然后统一引入。

  1. import { combineReducers } from 'redux'
  2. import * as reducers from './reducers'
  3. const reducer = combineReducers(reducers)

2. 纯函数:

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

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

由于Reducer是纯函数, 就可以保证同样的State,必定得到同样的View。所以Reducer函数里面不能改变State, 必须返回一个全新的对象

  1. // State 是一个对象
  2. function reducer(state, action) {
  3. return Object.assign({}, state, { thingToChange });
  4. // 或者
  5. return { ...state, ...newState };
  6. }
  7. // State 是一个数组
  8. function reducer(state, action) {
  9. return [...state, newItem];
  10. //state.push(newItem) //这样是不允许的, 因为改变了原来的state.
  11. }

最好把state对象设置为只读的。你没法改变他, 要得到新的State, 唯一的办法就是生成一个新的对象。 这样的好处就是,任何时候, 与某个View对应的State总是一个不变的对象。

4. applyMiddleware():

应用上基于redux的中间件(插件库)

  1. //combineReducers的作用是把多个reducer合并成一个reduer
  2. const allReducer = combineReducers({
  3. he:countReducer,
  4. persons:addPerson
  5. })
  6. //thunk的作用是允许store处理异步函数
  7. export default createStore(allReducer, applyMiddleware(thunk));

1.4 工作流程

redux原理图.png

  • 第一步: 用户发出Action

store.dispatch(action)

  • 第二部:Store自动调用reducer,并且传入两个参数:当前state和收到的actionReducer会返回一个新的State.

let nextState = todoApp(prevState, action);

  • 第三步:State 一旦有变化,Store 就会调用监听函数

store.subscribe(listener);

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

    1. function listerner() {
    2. let newState = store.getState();
    3. component.setState(newState);
    4. }

    2. react-redux

    Redux将所有的组件分成两大类:UI组件容器组件

    2.1 UI组件

    UI组件有以下几个特征:

  • 只负责UI的呈现, 不带有任何业务逻辑;

  • 没有状态
  • 所有数据都有参数提供的
  • 不适用任何Redux的API

    1. const Title =
    2. value => <h1>{value}</h1>;
  • 因为不含有状态,UI 组件又称为”纯组件“,即它纯函数一样,纯粹由参数决定它的值。

    2.2 容器组件

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

  • 负责管理数据和业务逻辑,不负责 UI 的呈现

  • 带有内部状态
  • 使用 Redux 的 API

react-redux模型图.png
总之,只要记住一句话就可以了:UI 组件负责 UI 的呈现容器组件负责管理数据和逻辑
React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。

2.3 connect()

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

  1. import { connect } from 'react-redux'
  2. const VisibleTodoList = connect(
  3. mapStateToProps,
  4. mapDispatchToProps
  5. )(TodoList)

connect方法接受两个参数:mapStateToPropsmapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action

2.4 Provider组件

让所有组件都可以得到state数据
connect方法生成容器组件以后, 需要让容器组件拿到state对象, 才能生成UI组件的参数。
一种解决方法是将state对象作为参数, 传入容器组件。 但是这样比较麻烦, 尤其是容器组件可能在很深的层级, 一级级将state传下去很麻烦。
React-Redux提供了Provider组件, 可以让容器组件拿到state。

  1. import { Provider } from 'react-redux'
  2. import { createStore } from 'redux'
  3. import todoApp from './reducers'
  4. import App from './components/App'
  5. let store = createStore(todoApp);
  6. render(
  7. <Provider store={store}>
  8. <App />
  9. </Provider>,
  10. document.getElementById('root')
  11. )

一般情况下在跟组件外面包了一层, 这样依赖, APP的所有子组件就默认都可以拿到state了。它的原理是connect组件的context属性

2.5 mapStateToProps()

mapStateToProps是一个函数。 他的作用是建立一个从(外部的)state对象到(UI组件的)Props对象映射关系。
作为函数, mapStateToProps执行后返回一个对象, 里面的每一个键值对就是一个映射。 :::success

  1. mapStateToProps函数返回的是一个对象;
  2. 返回的对象中的key就作为传递给UI组件的props的key,value就作为传递给UI组件的props的value;
  3. mapStateToProps用于传递状态; :::

    1. const mapStateToProps = (state) => {
    2. return {
    3. fetchUpdate: state.fetchUpdate
    4. }
    5. }

    mapStateToProps会订阅store,每当state更新的时候, 就会自动执行, 重新计算UI组件的参数, 从而触发UI组件的重新渲染
    mapStateToProps的第一个参数总是state对象, 但是它可以有第二个参数, 代表容器组件的props对象

    1. const mapStateToProps = (state, ownProps) => {
    2. return {
    3. active: ownProps.filter === state.visibility
    4. }
    5. }

    使用ownProps作为参数后, 如果容器组件的参数发生变化, 也会引发UI组件重新渲染。
    connect方法可以省略mapStateToProps参数, 那样的话, UI组件就不会订阅Store, 也就是说Store的更新不会引起UI组件的更新。

    2.6 mapDispachToProps()

    mapDispachToPropsconnect函数的第二个参数, 用来建立UI组件的参数跟store.dispatch方法的映射。 也就是说, 它定义了哪些用户的操作应该当作Action, 传给store.它可以是一个函数, 也可以是一个对象
    如果mapDispachToProps是一个函数, 会得到dispachownProps(容器组件的props对象)两个参数。 :::success

  4. mapDispatchToProps函数返回的是一个对象

  5. 返回的对象中的key就是作为传递给UI组件props的key,value就作为传递给UI组件props的value
  6. mapDispatchToProps用于传递操作状态的方法 :::
    1. const mapDispatchToProps = (
    2. dispatch,
    3. ownProps
    4. ) => {
    5. return {
    6. onClick: () => {
    7. dispatch({
    8. type: 'SET_VISIBILITY_FILTER',
    9. filter: ownProps.filter
    10. });
    11. }
    12. };
    13. }
    如果mapDispachToProps是一个对象, 它的每个键名也是对应UI组件的同名参数, 键值应该是一个函数, 会被当作Action creator, 返回的Action 会由Redux自动发出。 ```javascript const mapDispatchToProps = (dispatch) => ({ fetchScheduleDetail: (query) => dispatch(fetchScheduleDetail(query)), })

export default connect(mapStateToProps, mapDispatchToProps)(SchedulingOverview);

  1. <a name="nALJ2"></a>
  2. ## 2.7 纯函数与高阶函数
  3. <a name="FMhjc"></a>
  4. ### 2.7.1 纯函数
  5. 1. 一类特别的函数: **只要是同样的输入**(实参),**必定得到同样的输出**(返回)<br />2. 必须遵守以下一些约束
  6. - **不得改写参数数据**
  7. - **不会产生任何副作用**,例如网络请求,输入和输出设备
  8. - **不能调用Date.now()或者Math.random()等不纯的方法 **
  9. 3. **redux**的**reducer**函数必须是**一个纯函数**
  10. <a name="Wpowj"></a>
  11. ### 2.7.2 高阶函数
  12. 1. 理解: 一类特别的函数
  13. - 情况1:** 参数是函数**
  14. - 情况2: **返回是函数**
  15. 2. 常见的高阶函数:
  16. - **定时器设置函数**
  17. - **数组的forEach()/map()/filter()/reduce()/find()/bind()**
  18. - **promise**
  19. - react-redux中的**connect函数**
  20. 3. 作用: **能实现更加动态, 更加可扩展的功能**
  21. ```javascript
  22. export default connect(
  23. state =>({ count: state }),
  24. {
  25. jia:incrementAction,
  26. jian:decrementAction,
  27. jiaAsync:incrementActionAsync
  28. }
  29. )(CountUI)

6.3 redux-devtools-extension的使用
npm install redux-devtools-extension
clipboard.png

2.8 代码优化

  1. const mapStateToprops = (state) => {
  2. console.log(state)
  3. return {
  4. sum: state
  5. }
  6. }
  7. const mapDispatchToProps = dispatch => {
  8. return {
  9. increment: data => dispatch(incremnet(data)),
  10. decrement : data => dispatch(decrement(data))
  11. }
  12. }
  13. //使用connect创建并暴露一个CountContainer容器组件
  14. export default connect(mapStateToprops,mapDispatchToProps)(CountContainer)
  1. //第一种优化
  2. const mapStateToprops = state => ({
  3. sum: state
  4. })
  5. const mapDispatchToProps = dispatch => ({
  6. increment: data => dispatch(incremnet(data)),
  7. decrement : data => dispatch(decrement(data))
  8. })
  9. export default connect(mapStateToprops,mapDispatchToProps)(CountContainer)
  10. //第二种优化
  11. export default connect(
  12. state => ({
  13. sum: state
  14. }),
  15. dispatch => ({
  16. increment: data => dispatch(incremnet(data)),
  17. decrement : data => dispatch(decrement(data))
  18. })
  19. )(CountContainer)
  20. //第三种优化 API层级得优化, 只传action, react-redux子根据你传得action自动做派发
  21. export default connect(
  22. state => ({
  23. sum: state
  24. }),
  25. {
  26. jia: incremnet,
  27. jian: decrement
  28. }
  29. )(CountContainer)

3. redux与react-redux的区别:

3.1 redux

flux的基本原则时单项数据流。
redux的定位:它是将flux函数式编程思想结合在一起形成的架构。
思想:视图状态是一一对应的, 所有的状态都保存在一个store对象中。它是react中进行state状态管理的JS库, 并不是react插件,一般时管理多个组件中的共享数据状态
redux的三个基本原则:

  • 唯一数据源:唯一的数据源指的是应用的状态数据应该只存储在唯一的store上。这个store的状态是一个树形的对象, 每个组件往往只是树形对象上的一部分数据, 而如何设计Store上状态的结构, 就是Redux应用的核心问题。
  • 保持状态只读:不能直接去修改状态, 要修改store的状态, 必须要通过派发一个action对象完成。改变状态的方法不是去修改状态上的值,而是创建一个的状态对象返回给Redux,由Redxu完成新的状态的组装。
  • 数据改变只能通过纯函数完成:这里说的纯函数就是Reducer.reducer(preState,action)。第一个参数是当前的状态,第二个参数action是接收到的action对象,而reducer函数要做的事情就是根据stateaction的值产生一个新的对象返回。注意reducer必须是一个纯函数,也就是说函数的返回值必须由参数和stateaction决定,而且不产生任何副作用,也不能修改state和action对象。

Redux API:

  • store: 就是一个数据池,一个应用只有一个;
  • state: 一个state对应一个View,只要State相同,View就相同。
  • action:用来描述发生了什么State的变化,会导致VIew的变化。但是用户接触不到state,只能接触到View。所以State的变化必须是VIew导致的。Action就是View发出的通知,表示State应用要发生变化了, Action是一个对象。 其中的type属性是必须的,表示Action名称。
  • dispatch: 它是view触发action的唯一方法;
  • reducer:view触发action后, state要发生变化,reducer就是改变state的处理曾, 它接收到action和state, 通过处理action来返回新的state;
  • subscribe: 监听。监听state,state变化view随之改变。

基本流程:

3.2 react-redux

React-Redux: React-Redux是Redux的官方React绑定库。它能够使你的React组件从store中读取数据,并且向store分发actions以更新数据。React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

  • UI组件:就像一个纯函数,没有state,不做数据处理,只关注view,长什么样子完全取决于接收了什么参数(props)容器组件:关注行为派发和数据梳理,把处理好的数据交给UI组件呈现;React-Redux规定,所有的UI组件都由用户提供,容器组件则是由React-Redux自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。
  • connect:这个方法可以从UI组件生成容器组件;但容器组件的定位是处理数据、响应行为,因此,需要对UI组件添加额外的东西,即mapStateToProps和mapDispatchToProps,也就是在组件外加了一层state;
  • mapStateToProps:一个函数, 建立一个从(外部的)state对象到(UI组件的)props对象的映射关系。 它返回了一个拥有键值对的对象;
  • mapDispatchToProps:用来建立UI组件的参数到store.dispatch方法的映射。 它定义了哪些用户的操作应该当作动作,它可以是一个函数,也可以是一个对象。

    3.3使用区别:

  1. redux和组件进行对接的时候是直接在组件中进行创建。react-redux是运用Provider将组件和store对接,使在Provider里的所有组件都能共享store里的数据,还要使用connect将组件和react连接。
    1. //在组件中使用redux创建store
    2. const store = createStore(reducer);
    3. store.subscribe(() => {
    4. console.log(store.getState())
    5. })
    ```javascript import React from ‘react’; import ReactDOM from ‘react-dom/client’; import ‘./index.css’; import App from ‘./App’; import reportWebVitals from ‘./reportWebVitals’; import store from ‘./redux/store’ import { Provider } from ‘react-redux’ const root = ReactDOM.createRoot(document.getElementById(‘root’)); root.render( );

//容器组件 export default connect(mapStateToProps, mapDispatchToProps)(AutoPublishService); ```

  1. 获取state的方式不一样

redux获取state是直接通过store.getState()。
react-redux获取state是通过mapStateToProps函数,只要state数据变化就能获取最新数据。

  1. 触发action的方式不一样。

redux是使用dispatch直接触发,来操作store的数据。
react-redux是使用mapDispathToProps函数然后在调用dispatch进行触发。