说明
RTK是在原有的redux基础上进行了二次封装,提供开箱即用的API,所以更像是他的一个类库。
对比来理解,他们俩一个像是js,另一个则像是jQuery。所以为什么不使用jQuery(RTK)来开发,上手简单。
Tips:对比redux学习是为了更好的理解RTK是怎么对redux进行封装的。
1,安装核心依赖包
redux:
安装:npm install redux
// 依赖包示例:"redux": "2.3"
RTK:
安装:npm install @reduxjs/toolkit
// 依赖包实例:"@reduxjs/toolkit": "^1.9.7"
2,定义状态和reducer函数
redux:
分别定义state状态和reducer函数,reducer函数接收当前的state和action对象。
// 定义初始状态
const initialState = {count: 0,
};// 编写reducer函数
function counterReducer(state = initialState, action) {switch (action.type) {case 'incremented':return { value: state.count + 1 }case 'decremented':return { value: state.count - 1 }default:return state}
}
RTK:
利用createSlice切片来简化action和reducer的创建,在createSlice中定义reducers对象,每个键值对代表一个action类型和对应的reducer函数。
一个createSlice切片可以理解为一个业务模块。
// 引入创建slice切片的函数
import { createSlice } from '@reduxjs/toolkit';// 创建一个切片,传入name、初始值、reducers函数
// createSlice是对createAction和createReducer的包装和更高层的抽象
const counterSlice = createSlice({name: 'counter',initialState: { count: 0 },reducers: {increment(state) {state.count += 1;},decrement(state) {state.count -= 1;},},
});
3,创建store
redux:
使用createStore函数。这个函数接受一个reducer函数作为参数,并返回一个新的Store对象。
// 引入创建store的函数
import { createStore } from 'redux'// 用reducer作为参数创建store
let store = createStore(counterReducer)
RTK:
使用configureStore函数替代createStore,将创建的切片对象的reducer属性作为参数传入,RTK 自动处理 Reducer 的合并。
// 引入创建store的函数
import { configureStore } from '@reduxjs/toolkit'// 通过将切片.reducer函数作为参数,创建store
const store = configureStore({reducer: counterSlice.reducer
})
4,dispatch派发action和subscribe订阅
redux:
订阅:通过store.subscribe方法来实现订阅。
分发:通过store.dispatch方法来实现分发,传入action
// store通过subscribe订阅
store.subscribe(() => console.log(store.getState()))// 调用store的dispatch方法改变state
store.dispatch({ type: 'increment' })
// {count: 1}
store.dispatch({ type: 'decrement' })
// {count: 1}
RTK:
订阅:也是通过store.subscribe方法来实现订阅。
分发:通过store.dispatch方法来实现分发,他传入的action通过切片对象的actions属性来解构获取。
// store通过subscribe订阅
store.subscribe(() => console.log(store.getState()))// 通过创建的切片counterSlice.actions解构获得各个reducer函数
export const { increment, decrement } = counterSlice.actions;// 调用store的dispatch方法改变state
store.dispatch(increment())
// {count: 1}
store.dispatch(decrement ())
// {count: 1}
5,创建 Selectors
Selectors 是函数,用于从 state 中选择数据。
RTK提供了createSelector函数,用于创建可缓存的selectors,以提高性能。
createSelector函数提供了 memoization 功能,这意味着如果传入的参数没有变化,它会返回缓存的结果,而不是重新计算。
使用selectors可以轻松地从state中获取数据,而无需担心如何正确地引用和更新 state。
redux :
手动编写选择器函数。
// Redux 选择器函数
function selectCount(state) {return state.count;
}const currentValue = selectCount(store.getState());
RTK :
使用createSelector函数创建一个 selector函数,该函数接收 state 作为参数,并返回所需的数据片段。
// RTK 选择器函数,counter是切片的name
export const selectCount = (state) => state.counter.count;
6,使用trunk分发异步action
redux:
需要引入redux-trunk中间件,然后手动编写async/await逻辑处理异步操作,并且手动派发三种不同的 action 来表示异步操作的不同阶段:开始pending、成功fulfilled和失败rejected
// 定义action creators
export const fetchDataBegin = () => ({type: 'FETCH_DATA_BEGIN',
});export const fetchDataSuccess = (data) => ({type: 'FETCH_DATA_SUCCESS',payload: data,
});export const fetchDataFailure = (error) => ({type: 'FETCH_DATA_FAILURE',payload: error,
});// redux-thunk进行异步action的分发
export const fetchData = () => async (dispatch) => {dispatch({ type: 'FETCH_DATA_BEGIN' });try {const response = await fetch('/api/data');const data = await response.json();dispatch({ type: 'FETCH_DATA_SUCCESS', payload: data });} catch (error) {dispatch({ type: 'FETCH_DATA_FAILURE', payload: error });}
};
在组件中调用接口函数
// 组件中调用接口
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { fetchData } from './actions';const MyComponent = () => {const dispatch = useDispatch();useEffect(() => {dispatch(fetchData());}, [dispatch]);return (<div>{/* 显示组件内容 */}</div>);
};export default MyComponent;
RTK:
内置了redux-trunk中间件,调用createAsyncThunk函数来实现,只返回一个promise,RTK会自动处理promise的三种状态。他会为你生成三个特殊的action类型:[type]/pending、[type]/fulfilled 和 [type]/rejected。这些特殊的action类型对应于异步操作的三个阶段。当然在组件中,可以使用useSelector钩子来访问这些状态。
// 引入createAsyncThunk函数
import { createAsyncThunk } from '@reduxjs/toolkit';export const fetchData = createAsyncThunk('fetchData', async () => {const response = await fetch('/api/data');const data = await response.json();return data;
});
在组件中调用接口函数,同时在组件中用useSelector钩子访问这些状态
// 组件中调用接口,基本相同
import ......
import { useSelector } from 'react-redux';
// ...useEffect(() => {dispatch(fetchData());}, [dispatch, count]); // 这里会有不同// ...// 在组件中用useSelector钩子访问这些状态
const userFetchStatus = useSelector((state) => state.user.fetch.status);
const user = useSelector((state) => state.user.fetch.fulfilled);switch (userFetchStatus) {case 'pending':return <p>Loading...</p>;case 'fulfilled':return <p>User fetched successfully: {JSON.stringify(user)}</p>;case 'rejected':return <p>Failed to fetch user: {userFetchStatus.error}</p>;default:return null;
}export default MyComponent;