赞
踩
npm i @reduxjs/toolkit react-redux redux-persist
import {configureStore, combineReducers } from '@reduxjs/toolkit' import CollapsedSlice from './features/CollapsedSlice' import LoadingSlice from './features/LoadingSlice'; import AsyncReduxSlice from './features/AsyncReduxSlice'; import CreateAsyncThunkSlice from './features/CreateAsyncThunkSlice'; import ListFilterSlice from './features/ListFilterSlice'; import RouterListenerSlice from './features/RouterListenerSlice'; //持久化数据 import { persistStore, persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER } from 'redux-persist' import storage from 'redux-persist/lib/storage'; const reducer = combineReducers({ CollapsedSlice,//相当于CollapsedSlice:CollapsedSlice LoadingSlice, RouterListenerSlice, AsyncReduxSlice, CreateAsyncThunkSlice, ListFilterSlice, }) const persistConfig = { key:'redux', storage:storage, whitelist:['CollapsedSlice'],//白名单只保存CollapsedSlice // blacklist:['CollapsedSlice'],//黑名单仅不保存CollapsedSlice } const persistedRedcer = persistReducer(persistConfig,reducer); const store = configureStore({ reducer:persistedRedcer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { //忽略了 Redux Persist 调度的所有操作类型。这样做是为了在浏览器控制台读取a non-serializable value was detected in the state时不会出现错误。 ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }) }) export const persistor = persistStore(store); export default store;
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import "./axios"; import { Provider } from 'react-redux'; import store,{persistor} from './redux/store' import { PersistGate } from "redux-persist/integration/react"; // import reportWebVitals from './reportWebVitals'; // npm i -g json-server // json-server --watch ./db.json --port 5000 const root = ReactDOM.createRoot(document.getElementById('root')); root.render( // <React.StrictMode> <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <App /> </PersistGate> </Provider> // </React.StrictMode> ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals // reportWebVitals();
使用createSlice方法创建一个slice。每一个slice里面包含了reducer和actions,可以实现模块化的封装。所有的相关操作都独立在一个文件中完成。
CollapsedSlice.js
import { createSlice } from '@reduxjs/toolkit'; const initialState = { collapsed:false } export const collapsedSlice = createSlice({ // 命名空间,在调用action的时候会默认的设置为action的前缀collapsedSlice/changeCollapsed name:'collapsedSlice', // 初始值 initialState:initialState, // 这里的属性会自动的导出为collapsedSlice.actions,在组件中可以直接通过dispatch进行触发 reducers:{ //{ payload }解构出来的payload是dispatch传递的数据对象 changeCollapsed(state,action){ // console.log(action) // { // "type":"collapsedSlice/changeCollapsed", // "payload":{ // "value":2 // } // } // 内置了immutable不可变对象来管理state,不用再自己拷贝数据进行处理 state.collapsed = !state.collapsed; } } }) // 导出actions export const { changeCollapsed } = collapsedSlice.actions; // 导出reducer,在创建store时使用到 export default collapsedSlice.reducer;
import { useSelector, useDispatch } from 'react-redux'; import {changeCollapsed} from '../../redux/features/CollapsedSlice' // 引入actions import { MenuUnfoldOutlined, MenuFoldOutlined, UserOutlined, } from "@ant-design/icons"; import React from 'react' import { Layout, Dropdown, Avatar,Button } from "antd"; import { useNavigate } from 'react-router'; const { Header} = Layout; export default function TopHeader() { //根据store.js中设置的reducer名字,从CollapsedSlice空间获取state const {collapsed} = useSelector(state=>state.CollapsedSlice); const dispatch = useDispatch(); const handleCollapsed = ()=>{ dispatch(changeCollapsed({value:2})); } const { username, role:{roleName} } = JSON.parse(localStorage.getItem("token")); const navigate = useNavigate(); const items = [ { key: '1', label: roleName, }, { key: '2', label: ( <Button type="primary" danger onClick={() => { localStorage.removeItem("token"); navigate("/login",{replace:true}); }} > 退出登录 </Button> ), }, ]; return ( <Header className="site-layout-background" style={{ paddingLeft: "16px" }}> {/* {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, { className: 'trigger', onClick: () => setCollapsed(!collapsed), })} */} { collapsed ? <MenuUnfoldOutlined onClick={handleCollapsed}/>: <MenuFoldOutlined onClick={handleCollapsed}/> } <div style={{ display: "inline", float: "right" }}> <span> 欢迎<span style={{ color: "#1890ff" }}>{username}</span>回来 </span> <Dropdown menu={{items}}> <div style={{display:'inline-block',cursor:'pointer'}} onClick={(e) => e.preventDefault()}> <Avatar size="large" icon={<UserOutlined />} /> </div> </Dropdown> </div> </Header> ) }
RTK集成了redux-thunk来处理异步事件,所以可以按照之前thunk的写法来写异步请求
AsyncReduxSlice.js
import { createSlice } from "@reduxjs/toolkit"; import axios from 'axios' const initialState = { list:[] } export const asyncReduxSlice = createSlice({ name:'asyncReduxSlice', initialState, reducers:{ changeList(state,{payload}){ state.list = payload.list; } } }) const {changeList} = asyncReduxSlice.actions; export const getList = payload => { return dispatch => { axios.get("/rights?_embed=children").then((res) => { dispatch(changeList({list:res.data})) }); } } export default asyncReduxSlice.reducer;
Test-AsyncRedux.js
import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux/es/exports' import {getList} from '../redux/features/AsyncReduxSlice' export default function TestAsyncRedux() { const {list} = useSelector(state=>state.AsyncReduxSlice); const dispatch = useDispatch(); useEffect(()=>{ console.log(111111) if(list.length === 0){ dispatch(getList()) }else{ alert('列表已被缓存!') } },[dispatch,list.length]); return ( <div> { list.map(item=>{ return <li key={item.id}>{item.title}</li> }) } </div> ) }
createAsyncThunk方法可以创建一个异步的action,这个方法被执行的时候会有三个( pending(进行中) fulfilled(成功) rejected(失败))状态。可以监听状态的改变执行不同的操作。以下代码示例中使用到了extraReducers创建额外的action对数据获取的状态信息进行监听。
CreateAsyncThunkSlice.js
import {createSlice,createAsyncThunk} from '@reduxjs/toolkit' import axios from 'axios' const getListAPI = ()=>{ return axios.get("/rights?_embed=children").then((res) => res.data); } export const loadList = createAsyncThunk( 'CATSlice/loadList', async ()=>{ const list = await getListAPI(); return list; } ) const initialState = { list:[] } export const CATSlice = createSlice({ name:'CATSlice', initialState, // 可以额外的触发其他slice中的数据关联改变 extraReducers: builder=>{ builder .addCase(loadList.pending,state=>{ console.log('进行中'); }) .addCase(loadList.fulfilled,(state,action)=>{ console.log('成功'); const { payload } = action; // { // type:'CATSlice/loadList/fulfilled', // payload://loadList返回值 // } console.log(action); state.list = payload; }) .addCase(loadList.rejected,(state, err)=>{ console.log('失败',err); }) } }) export default CATSlice.reducer;
Test-CreateAsyncThunk.js
import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux/es/exports' import {loadList} from '../redux/features/CreateAsyncThunkSlice' export default function TestAsyncRedux() { const {list} = useSelector(state=>{ console.log(state) return state.CreateAsyncThunkSlice }); const dispatch = useDispatch(); useEffect(()=>{ console.log(11111111) if(list.length === 0){ dispatch(loadList()) }else{ alert('列表已被缓存!') } },[]);// eslint-disable-line return ( <div> { list.map(item=>{ return <li key={item.id}>{item.title}</li> }) } </div> ) }
当useSelector方法涉及到复杂逻辑运算时,且返回一个对象的时候,每次运行都返回了一个新的引用值,会使组件重新渲染,即使返回的数据内容并没有改变
为了解决这个问题,可以使用Reselect库,它是一个创建记忆化selector的库,只有在输入发生变化时才会重新计算结果,rtk正是集成了这个库,并把它导出为createSelector函数
引用的CreateAsyncThunkSlice.js参照上面
ListFilterSlice.js
import { createSlice } from '@reduxjs/toolkit'; const initialState = { listFilter:'/home' } export const listFilterSlice = createSlice({ // 命名空间,在调用action的时候会默认的设置为action的前缀listFilterSlice/changelistFilter name:'listFilterSlice', // 初始值 initialState:initialState, // 这里的属性会自动的导出为listFilterSlice.actions,在组件中可以直接通过dispatch进行触发 reducers:{ //{ payload }解构出来的payload是dispatch传递的数据对象 changelistFilter(state,action){ // console.log(action) // { // "type":"listFilterSlice/changelistFilter", // "payload":{ // "value":2 // } // } // 内置了immutable不可变对象来管理state,不用再自己拷贝数据进行处理 state.listFilter = !state.listFilter; } } }) // 导出actions export const { changelistFilter } = listFilterSlice.actions; // 导出reducer,在创建store时使用到 export default listFilterSlice.reducer;
Test-CreateSelector.js
import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux/es/exports' import {loadList} from '../redux/features/CreateAsyncThunkSlice' import { createSelector } from '@reduxjs/toolkit'; //当useSelector方法涉及到复杂逻辑运算时,且返回一个对象的时候,每次运行都返回了一个新的引用值,会使组件重新渲染,即使返回的数据内容并没有改变 //为了解决这个问题,可以使用Reselect库,它是一个创建记忆化selector的库,只有在输入发生变化时才会重新计算结果,rtk正是集成了这个库,并把它导出为createSelector函数 const selectList = state=>state.CreateAsyncThunkSlice; const selectFilter = state=>state.ListFilterSlice; const filterList = createSelector(selectList,selectFilter,(listState,filterState)=>{ const {list} = listState; const {listFilter} = filterState; console.log(listState,filterState) switch(listFilter){ case 'all': return list; case '/home': return list.filter(item=>item.key === listFilter); default : throw new Error('Unknown filter: ' + listFilter); } }) export default function TestAsyncRedux() { const mapList = useSelector(state => filterList(state)); const dispatch = useDispatch(); useEffect(()=>{ console.log(11111111) if(mapList.length === 0){ dispatch(loadList()) }else{ alert('列表已被缓存!') } },[]);// eslint-disable-line return ( <div> { mapList.map(item=>{ return <li key={item.id}>{item.title}</li> }) } </div> ) }
RTK中已经配置了redux-devtools,所以只要浏览器安装调试工具redux-devtools-extension
安装参考redux-devtools-extension
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。