Redux的使用详解(二)

121 阅读7分钟

组件中异步操作

redux中保存的state是一个本地定义的数据

  • 可以直接通过同步的操作来dispatch actionstate就会被立即更新。
  • 但是真实开发中,redux中保存的很多数据可能来自服务器,我们需要进行异步的请求,再将数据保存到redux中。
  • 网络请求可以在class组件的componentDidMount中发送
 componentDidMount() {
    axios.get("http://123.207.32.32:8000/home/multidata").then(res => {
      const banners = res.data.data.banner.list
      const recommends = res.data.data.recommend.list

      this.props.changeBanners(banners)
      this.props.changeRecommends(recommends)
    })
  }

redux中异步操作

组件中异步操作的缺陷

  • 必须将网络请求的异步代码放到组件的生命周期中来完成;
  • 事实上,网络请求到的数据也属于我们状态管理的一部分,更好的一种方式应该是将其也交给redux来管理;
image.png

redux中的同步操作

  • 执行了dispatch函数之后,对应的reducer函数收到action对象后立即得到执行,reducer执行完了之后,state立即就改变了,此时store.getState函数,取到的是最新的值

redux的异步操作

  • 原则上redux并没有提供异步action的处理方案,异步action需要依赖第三方的中间件解决- redux-thunk

  • dispatch一个异步函数,目标state不会立即响应,而是要看异步函数内部的逻辑,来决定state什么时候响应

  • 代码段展示了如何在 Redux 中使用 redux-thunk 中间件来处理异步操作,并结合使用 Redux DevTools 进行调试。

    1. mapDispatchToProps 函数定义了一个名为 fetchHomeMultidata 的方法,它通过派发 fetchHomeMultidataAction 来触发异步操作。
    2. fetchHomeMultidataAction 方法是一个返回函数的函数。这是 redux-thunk 的特性,它允许我们返回一个函数而不是普通的 action 对象。在这个例子中,返回的函数接受 dispatchgetState 参数。
    3. 在返回的函数内部,我们可以执行异步操作,例如发起网络请求。在示例中,我们使用 axios 发起一个 GET 请求,并在响应返回后将数据派发给其他 action。
    4. 在创建 Redux store 时,我们使用了 applyMiddleware 方法并传入 thunk 中间件。这样,我们启用了 redux-thunk 中间件,使得我们可以在派发 action 时传递一个函数。
    5. 我们还使用了 Redux DevTools 扩展来增强 Redux 的开发体验。通过 composeEnhancers 函数,我们将 DevTools 扩展和中间件应用于 Redux store 的创建过程。

总结起来,通过使用 redux-thunk 中间件,我们可以在 Redux 中处理异步操作。返回的函数可以执行异步任务,并在完成后通过派发其他 action 来更新应用程序的状态。同时,使用 Redux DevTools 扩展可以帮助我们进行调试和监控 Redux 应用程序的状态变化。

//派发函数
const mapDispatchToProps = (dispatch) => ({
  fetchHomeMultidata() {
    dispatch(fetchHomeMultidataAction())
  }
})

//本来应该返回对象,怎么让其派发函数呢
export const fetchHomeMultidataAction = () => {
  // 如果是一个普通的action, 那么我们这里需要返回action对象
  // 问题: 对象中是不能直接拿到从服务器请求的异步数据的
  // return {}

  return function(dispatch, getState) {
    // 异步操作: 网络请求
    // console.log("foo function execution-----", getState().counter)
    axios.get("http://123.207.32.32:8000/home/multidata").then(res => {
      const banners = res.data.data.banner.list
      const recommends = res.data.data.recommend.list

      // dispatch({ type: actionTypes.CHANGE_BANNERS, banners })
      // dispatch({ type: actionTypes.CHANGE_RECOMMENDS, recommends })
      dispatch(changeBannersAction(banners))
      dispatch(changeRecommendsAction(recommends))
    })
  }

  // 如果返回的是一个函数, 那么redux是不支持的
  // return foo
}

//redux-thunk
import { createStore, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk"
import reducer from "./reducer"

// 正常情况下 store.dispatch(object)
// 想要派发函数 store.dispatch(function)

// redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

使用redux-thunk

  1. 在创建store时传入应用了middlewareenhance函数
    • 通过applyMiddleware来结合多个Middleware, 返回一个enhancer
    • enhancer作为第二个参数传入到createStore中;
const enhancer = applyMiddleware(thunkMiddleware)
const store = createStore(reducer,enhancer)
  1. 定义返回一个函数的action
    • 注意:这里不是返回一个对象了,而是一个函数;
    • 该函数在dispatch之后会被执行;
export const fetchHomeMultidataAction = () => {
  // 如果是一个普通的action, 那么我们这里需要返回action对象
  // 问题: 对象中是不能直接拿到从服务器请求的异步数据的
  // return {}

  return function(dispatch, getState) {
    // 异步操作: 网络请求
    // console.log("foo function execution-----", getState().counter)
    axios.get("http://123.207.32.32:8000/home/multidata").then(res => {
      const banners = res.data.data.banner.list
      const recommends = res.data.data.recommend.list

      // dispatch({ type: actionTypes.CHANGE_BANNERS, banners })
      // dispatch({ type: actionTypes.CHANGE_RECOMMENDS, recommends })
      dispatch(changeBannersAction(banners))
      dispatch(changeRecommendsAction(recommends))
    })
  }

  // 如果返回的是一个函数, 那么redux是不支持的
  // return foo
}

redux-devtools

  1. 在redux中继承devtools的中间件
image.png

中间件扩展(Middleware)

中间件

  • redux也引入了中间件(Middleware)的概念:
    • 这个中间件的目的是在dispatchaction和最终达到的reducer之间,扩展一些自己的代码;
    • 比如日志记录、调用异步接口、添加代码调试功能等等;
  • 我们要做的事情就是发送异步的网络请求,所以我们可以添加对应的中间件:
    • 这里官网推荐的、包括演示的网络请求的中间件是使用 redux-thunk
  • redux-thunk 是如何做到让我们可以发送异步的请求呢?
    • 默认情况下的dispatch(action),action需要是一个JavaScript的对象
    • redux-thunk可以让dispatch(action函数),action可以是一个函数
    • 该函数会被调用,并且会传给这个函数一个dispatch函数和getState函数;
      • dispatch函数用于我们之后再次派发action
      • getState函数考虑到我们之后的一些操作需要依赖原来的状态,用于让我们可以获取之前的一些状态;

中间件总结

中间件(middleware)是在应用程序中处理请求和响应之间的中间层。在 Web 开发中,中间件通常用于处理请求、修改响应、执行日志记录、认证授权等功能。

在前端开发中,中间件通常与 Redux 或 Redux-like 状态管理库一起使用。Redux 中间件是在派发(dispatch)一个 action 和该 action 最终被 reducer 处理之间的拦截器。它可以截获派发的 action,并对其进行处理、修改或触发其他操作,然后再将其传递给下一个中间件或最终到达 reducer。

中间件提供了一种可扩展 Redux 功能的机制,可以用于实现异步操作、日志记录、错误处理、路由导航等功能。常见的 Redux 中间件包括 Redux Thunk、Redux Saga、Redux Promise 等。

以下是一个简单的 Redux 中间件示例,用于记录每次派发的 action 和状态变化:

const loggerMiddleware = (store) => (next) => (action) => {
  console.log('Dispatching action:', action);
  
  // 调用下一个中间件或最终的 reducer 处理函数
  const result = next(action);
  
  console.log('Updated state:', store.getState());
  
  return result;
};

// 应用中间件
const middlewareEnhancer = applyMiddleware(loggerMiddleware);
const store = createStore(reducer, initialState, middlewareEnhancer);

在上述示例中,loggerMiddleware 是一个简单的中间件函数,它接收 store 对象作为参数,并返回一个新的函数。这个新函数接收 next 参数,它是一个回调函数,表示下一个中间件或最终的 reducer 处理函数。最后返回的函数接收 action 参数,表示派发的 action

在中间件函数中,我们可以在派发 action 前后执行自定义逻辑。在这个示例中,我们在派发 action 前打印出 action 的内容,然后调用 next(action) 将其传递给下一个中间件或 reducer 处理函数,并获取返回结果。然后,在 action 被处理后,我们再次打印出更新后的状态。

通过将中间件应用到 Redux store 上,我们可以在派发 actionreducer 处理之间注入自定义的逻辑,以实现各种功能需求。

redux中如何进行reducer的拆分

  • 主要利用模块化的思想,将不同的数据拆分到不同的模块

  • 每一模块都有自己的目录结构

    • reducer ---> 接受action对象,返回最新的state
    • constants ---> 定义常量数据
    • actoinCreator ---> 定义创建action对象的函数
    • index ---> 导出reducer
  • store中的index文件

    • 合并reducer,导出store实例
image.png

combineReducers函数

  1. redux给我们提供了一个combineReducers函数可以方便的让我们对多个reducer进行合并:
// 将多个reducer合并在一起
const reducer = combineReducers({
  counter: counterReducer,
  home: homeReducer,
  user: userReducer
})

const store = createStore(reducer)
export default store
  1. combineReducers是如何实现的呢?
    • 事实上,它也是将我们传入的reducers合并到一个对象中,最终返回一个combination的函数(相当于我们之前的reducer函数了);
    • 在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state
    • 新的state会触发订阅者发生对应的刷新,而旧的state可以有效的组织订阅者发生刷新;