组件中异步操作
redux
中保存的state
是一个本地定义的数据
- 可以直接通过同步的操作来
dispatch
action
,state
就会被立即更新。 - 但是真实开发中,
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来管理;
redux中的同步操作
- 执行了
dispatch
函数之后,对应的reducer
函数收到action
对象后立即得到执行,reducer
执行完了之后,state
立即就改变了,此时store.getState
函数,取到的是最新的值
redux的异步操作
-
原则上redux并没有提供异步action的处理方案,异步action需要依赖第三方的中间件解决-
redux-thunk
-
dispatch一个异步函数,目标state不会立即响应,而是要看异步函数内部的逻辑,来决定state什么时候响应
-
代码段展示了如何在 Redux 中使用 redux-thunk 中间件来处理异步操作,并结合使用 Redux DevTools 进行调试。
mapDispatchToProps
函数定义了一个名为fetchHomeMultidata
的方法,它通过派发fetchHomeMultidataAction
来触发异步操作。fetchHomeMultidataAction
方法是一个返回函数的函数。这是 redux-thunk 的特性,它允许我们返回一个函数而不是普通的 action 对象。在这个例子中,返回的函数接受dispatch
和getState
参数。- 在返回的函数内部,我们可以执行异步操作,例如发起网络请求。在示例中,我们使用 axios 发起一个 GET 请求,并在响应返回后将数据派发给其他 action。
- 在创建 Redux store 时,我们使用了
applyMiddleware
方法并传入thunk
中间件。这样,我们启用了 redux-thunk 中间件,使得我们可以在派发 action 时传递一个函数。 - 我们还使用了 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
- 在创建
store
时传入应用了middleware
的enhance
函数- 通过
applyMiddleware
来结合多个Middleware
, 返回一个enhancer
; - 将
enhancer
作为第二个参数传入到createStore
中;
- 通过
const enhancer = applyMiddleware(thunkMiddleware)
const store = createStore(reducer,enhancer)
- 定义返回一个函数的
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
- 在redux中继承devtools的中间件
中间件扩展(Middleware)
中间件
redux
也引入了中间件(Middleware
)的概念:- 这个中间件的目的是在
dispatch
的action
和最终达到的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 上,我们可以在派发 action
和 reducer
处理之间注入自定义的逻辑,以实现各种功能需求。
redux中如何进行reducer的拆分
-
主要利用模块化的思想,将不同的数据拆分到不同的模块
-
每一模块都有自己的目录结构
- reducer ---> 接受action对象,返回最新的state
- constants ---> 定义常量数据
- actoinCreator ---> 定义创建action对象的函数
- index ---> 导出reducer
-
store中的index文件
- 合并reducer,导出store实例
combineReducers函数
- redux给我们提供了一个
combineReducers
函数可以方便的让我们对多个reducer
进行合并:
// 将多个reducer合并在一起
const reducer = combineReducers({
counter: counterReducer,
home: homeReducer,
user: userReducer
})
const store = createStore(reducer)
export default store
- combineReducers是如何实现的呢?
- 事实上,它也是将我们传入的
reducers
合并到一个对象中,最终返回一个combination
的函数(相当于我们之前的reducer函数了); - 在执行
combination
函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state
还是新的state
; - 新的
state
会触发订阅者发生对应的刷新,而旧的state
可以有效的组织订阅者发生刷新;
- 事实上,它也是将我们传入的