Réapprentissage de l'analyse de gestion d'état de React (redux)

Nous savons que lorsque notre projet grandit, nous devons nous appuyer sur beaucoup de données, et les données seront utilisées dans divers composants, nous devons donc partager ces données pour mettre à jour la page de manière réactive. Document chinois s'il vous plaît voir ici

Contenu de base Redux

action

  • Redux nous oblige à mettre à jour les données via des actions. Toutes les modifications de données doivent être mises à jour via l'action de répartition.
  • action est un objet JavaScript simple qui décrit le type et la valeur de cette mise à jour.
    import {INCREMENT, DECREMENT} from './constants'
    // 返回一个对象{type, value}

    export const increment = num => ({
      type: INCREMENT,
      value: num
    })
    export const decrement = num => ({
      type: DECREMENT,
      value: num
    })
复制代码

réducteur

  • Un réducteur est une fonction pure. Liez l'état et l'action ensemble.
  • Ce que fait le réducteur, c'est combiner l'état entrant et l'action pour générer un nouvel état.
    import {INCREMENT, DECREMENT} from './constants'
    export default function reducer(state = {count: 0}, action) {
      switch (action.type) {
        case INCREMENT:
          return {...state, count: state.count + action.value}
        case DECREMENT:
          return {...state, count: state.count - action.value}
        default:
          return state
      }
    }
复制代码

boutique

  • Fournir des variables et des méthodes accessibles.
    import {createStore} from 'redux';
    import reducer from './reducer'

    const store = createStore(reducer);

    export default store;
复制代码

Trois principes de Redux

source unique de vérité

  • L'état de l'ensemble de l'application est stocké dans une arborescence d'objets, et cette arborescence d'objets n'est stockée que dans un magasin.
  • Redux n'impose pas que nous ne pouvons pas créer plusieurs magasins, mais cela n'est pas propice à la maintenance des données.
  • Une seule source de données facilite la maintenance, le suivi et la modification de l'état de l'ensemble de l'application.

L'état est en lecture seule

  • La seule façon de modifier l'état doit être de déclencher une action, n'essayez pas de modifier l'état de quelque manière que ce soit ailleurs.
  • Cela garantit que ni la vue ni les requêtes réseau ne peuvent directement modifier l'état, elles peuvent uniquement décrire comment elles souhaitent modifier l'état par le biais d'actions.
  • Cela garantit que toutes les modifications sont centralisées et exécutées dans un ordre strict, vous n'avez donc pas à vous soucier des conditions de course.

Utiliser des fonctions pures pour effectuer des modifications

  • L'ancien état et les actions sont liés par le réducteur et un nouvel état est renvoyé.
  • Au fur et à mesure que la complexité de l'application augmente, nous pouvons diviser le réducteur en plusieurs réducteurs plus petits qui fonctionnent sur différentes parties de l'arbre d'état.
  • Mais tous les réducteurs doivent être des fonctions pures sans aucun effet secondaire.

Ce qui suit est une image fournie par le site officiel de redux.image.png

cas de base

下面我们来看一下在项目中如何使用吧。这个案例就是点击按钮,调整数字,然后响应在两个组件中。

文件结构

image.png

代码

  • actionCreators.js: 定义action对象。写成函数,方便扩展。
    import {INCREMENT, DECREMENT} from './constants'
    // 返回一个对象{type, value}

    export const increment = num => ({
      type: INCREMENT,
      value: num
    })
    export const decrement = num => ({
      type: DECREMENT,
      value: num
    })
复制代码
  • constants.js: 定义action常量,防止多处使用写错。
    export const INCREMENT = "increment"
    export const DECREMENT = "decrement"
复制代码
  • index.js
    import {createStore} from 'redux';
    import reducer from './reducer'

    const store = createStore(reducer);

    export default store;
复制代码
  • reducer.js
    import {INCREMENT, DECREMENT} from './constants'
    export default function reducer(state = {count: 0}, action) {
      switch (action.type) {
        case INCREMENT:
          return {...state, count: state.count + action.value}
        case DECREMENT:
          return {...state, count: state.count - action.value}
        default:
          return state
      }
    }
复制代码
  • 组件一
import React, { PureComponent } from 'react'
import { increment, decrement } from '../store/actionCreators.js'
import store from '../store'
export default class About extends PureComponent {
  constructor(props) {
    super(props)
    this.state = store.getState()
  }

  componentDidMount() {
    store.subscribe(() => {
      this.setState(store.getState())
    })
  }

  decrement(num) {
    store.dispatch(decrement(num))
  }
  increment(num) {
    store.dispatch(increment(num))
  }

  render() {
    return (
      <div>
        <hr />
        <div>about</div>
        <div>{this.state.count}</div>
        <button onClick={(e) => this.decrement(5)}>-5</button>
        <button onClick={(e) => this.increment(5)}>+5</button>
      </div>
    )
  }
}
复制代码
  • 组件二
import React, { PureComponent } from 'react'
import { increment, decrement } from '../store/actionCreators.js'
import store from '../store'
export default class Home extends PureComponent {
  constructor(props) {
    super(props)
    this.state = store.getState()
  }

  componentDidMount() {
    store.subscribe(() => {
      this.setState(store.getState())
    })
  }

  decrement(num) {
    store.dispatch(decrement(num))
  }
  increment(num) {
    store.dispatch(increment(num))
  }

  render() {
    return (
      <div>
        <div>home</div>
        <div>{this.state.count}</div>
        <button onClick={(e) => this.decrement(5)}>-5</button>
        <button onClick={(e) => this.increment(5)}>+5</button>
      </div>
    )
  }
}
复制代码

btn2.gif

封装高阶组件

从上面的代码可以看出,有很多重复的代码逻辑,所以我么可以抽离代码,让其变的更简洁。

  • mapStateToProps。高阶组件中,调用该函数并传入state。

其实我们的逻辑还是在组件中书写的,即mapStateToProps的方法体。

  • mapDispatchToProps。高阶组件中,调用该函数并传入store.dispatch。

其实我们的action函数依旧是在组件中写的,只是我们在高阶组件中触发而已。

    import React, { PureComponent } from 'react'
    import store from '../store'

    export default function StoreHOC(mapStateToProps, mapDispatchToProps) {
      return (WrappedComponent) => {
        class HoComponent extends PureComponent {
          constructor(props) {
            super(props)
            this.state = store.getState()
          }

          componentDidMount() {
            store.subscribe(() => {
              this.setState(store.getState())
            })
          }
          render() {
            return (
              <WrappedComponent
                {...this.props}
                {...mapStateToProps(this.state)}
                {...mapDispatchToProps(store.dispatch)}
              />
            )
          }
        }
        return HoComponent
      }
    }
复制代码

使用

    import React, { PureComponent } from 'react'
    import { increment, decrement } from '../store/actionCreators.js'

    import StoreHOC from './StoreHOC'
    class Home extends PureComponent {


      render() {
        return (
          <div>
            <div>home</div>
            <div>{this.props.count}</div>
            <button onClick={(e) => this.props.decrement(5)}>-5</button>
            <button onClick={(e) => this.props.increment(5)}>+5</button>
          </div>
        )
      }
    }

    const mapStateToProps = (state) => ({
      ...state
    })
    const mapDispatchToProps = (dispatch) => ({
      decrement(num) {
        dispatch(decrement(num))
      },
      increment(num) {
        dispatch(increment(num))
      }
    })

    export default StoreHOC(mapStateToProps, mapDispatchToProps)(Home)
复制代码

再次优化

但是上面代码的耦合性还是挺高的,必须的引入store。如果你想让别人使用这个高阶组件,那么他们也需要在你的代码中导入store。所以就有了下面的封装。

我们可以通过React.createContext。

    // context.js
    import  {createContext} from 'react';

    const StoreContext = createContext();
    export default StoreContext
复制代码

高阶组件

  • 就是将store换成this.context
import React, { PureComponent } from 'react'
- // import store from '../store'
import StoreContext from './context.js'

export default function StoreHOC(mapStateToProps, mapDispatchToProps) {
  return (WrappedComponent) => {
    class HoComponent extends PureComponent {
      + static contextType = StoreContext
      + constructor(props, context) {
        super(props, context)
        this.state = context.getState()
      }

      componentDidMount() {
       - // 需要跟新state
       - // store.subscribe(() => {
       - //   this.setState(store.getState())
       - // })
       + this.context.subscribe(() => {
       +   this.setState(this.context.getState())
       + })
      }
      render() {
        return (
          <WrappedComponent
            {...this.props}
            {...mapStateToProps(this.state)}
            + {...mapDispatchToProps(this.context.dispatch)}
          />
        )
      }
    }
    return HoComponent
  }
}
复制代码

使用


    import StoreContext from './storeTest/context'
    import store from './store'
    ReactDOM.render((
        <div>
          <StoreContext.Provider value={store}>
            <Home></Home>
            <About></About>
          </StoreContext.Provider>
        </div>
    ), document.getElementById("root"))
复制代码

使用react-redux

但是在工作中,我们都会使用react-redux库。

安装

    npm install react-redux
复制代码

使用

使用方法和上面我们自己封装的高阶函数一样。只不过我们给Provider组件设置共享数据时,是使用store,而非value。

    import React from 'react'
    import ReactDOM from 'react-dom'
    import './index.css'
    import App from './App'
    import store from './app/store'
    import { Provider } from 'react-redux'

    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    )
复制代码

redux处理异步

我们只打,当我们做项目时,需要请求数据,所以就需要发送网络请求获取数据。那么如何在redux中处理异步请求呢?

网络请求位置

  • 可以放在组件的生命周期函数中。
  • 可以放在redux中。

但是网络请求到的数据也属于我们状态管理的一部分,更好的应该是将其也交给redux来管理。所以我们就需要使用redux-thunk来完成这些操作。

redux-thunk的介绍

  • 默认情况下的dispatch(action),action需要是一个JavaScript的对象。
  • redux-thunk peut faire dispatch (fonction d'action), action peut être une fonction.
    • Cette fonction sera appelée automatiquement et recevra une fonction dispatch et une fonction getState à cette fonction.
    • La fonction dispatch nous permet de redistribuer l'action plus tard.
    • La fonction getState tient compte du fait que certaines de nos opérations ultérieures doivent s'appuyer sur l'état d'origine, afin que nous puissions obtenir certains états précédents.

utilisation spécifique

Nous avons besoin de applyMiddleware intégré à redux pour utiliser redux-thunk.

    import {createStore, applyMiddleware, compose} from 'redux'

    import reducer from './reducer'

    import thunk from 'redux-thunk'
    
    // 这里也使用了redux-devtools工具
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) : compose

    const enhancer = composeEnhancers(applyMiddleware(thunk))
    const store = createStore(
      reducer,
      enhancer
    )

    export default store
复制代码

Envoyer des requêtes réseau dans les opérations d'action.

    export const getBannerList = (dispatch, getState) => {
      axios({
        url: 'http://123.207.32.32:8000/home/multidata'
      }).then((res) => {
      // 二次dispatch
        dispatch({
            type: "getBannerList",
            value: res.data.data.banner.list
          })
      })
    }
复制代码

un peu de logique dans le composant

    componentDidMount() {
    // 调用映射后的action函数。实现一次dispatch。
        this.props.getBannerList()
    }
    const mapDispatchToProps = (dispatch) => ({
      getBannerList() {
        dispatch(getBannerList)
      }
    })
    export default connect(mapStateToProps, mapDispatchToProps)(Home)
复制代码

Analyse simple du middleware redux

    function thunk(store) {
      const next = store.dispatch;
      // 把它当做dispatch函数
      function dispatchAndThunk(action) {
        if(typeof action === "function") {
          action(store.dispatch, store.getState)
        }else {
          next(action)
        }
      }
      // 方式一
      // store.dispatch = dispatchAndThunk
      // 方式二
      return dispatchAndThunk
    }


    function applyMiddleware(...middleware) {
      for(let i = 0; i < middleware.length; i++) {
      // 方式一
      // middleware[i](store)
      // 方式二
        store.dispatch = middleware[i](store)
      }
    }
    // 可以传入多个中间件函数
    applyMiddleware(thunk)
复制代码

réducteur fendu

Nous avons constaté qu'à mesure que le projet se développe et que la quantité de données augmente, si tous les états sont gérés dans un réducteur, le réducteur deviendra également très gonflé. Mauvais pour l'entretien. Nous devons donc diviser le réducteur.

Comment le diviser ?

Les étudiants qui ont étudié vuex savent qu'il existe un concept de modules. Nous pouvons donc également diviser les données correspondantes dans un module, et laisser chacun maintenir ses propres fichiers de réducteur, actionCreator, constantes. Fusionner ensuite dans le réducteur total. image.pngAlors, comment fusionner dans le réducteur total?

À ce stade, nous devons utiliser la fonction combineReducers fournie par redux.image.png

Cette partie de redux est encore relativement difficile à comprendre, et elle ne sera pas utilisée telle quelle comme vuex. Donc, vous devez toujours bien comprendre redux et beaucoup vous entraîner.

Je suppose que tu aimes

Origine juejin.im/post/7079277020521185293
conseillé
Classement