ximingx(帅鸿宇)

ximingx(帅鸿宇)

redux

1. 使用案例

index.js

import { createStore, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk'

import CounterReducer from './counter';
import HomeReducer from './home';

const reducer = combineReducers({
    counter: CounterReducer,
    home: HomeReducer
})

const store = createStore(reducer, applyMiddleware(thunk));

export default store;

name/action.js

import * as actionTypes from './const'
import axios from 'axios'

export const changeBannersAction = (banners) => ({
    type: actionTypes.CHANGE_BANNERS,
    banners
})

export const changeRecommendsAction = (recommends) => ({
    type: actionTypes.CHANGE_RECOMMENDS,
    recommends
})

export const getHomeMultidataAction = (dispatch) => {
    return (dispatch) => {
        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(changeBannersAction(banners))
            dispatch(changeRecommendsAction(recommends))
        })
    }
}

name/const.js

export const CHANGE_BANNERS = 'changeBanners';
export const CHANGE_RECOMMENDS = 'changeRecommends';

name/index.js

import reducer from './reducer'

export * from './action';

export default reducer;

name/reducer.js

import * as types from './const';

const initialState = {
    banners: [],
    recommends: []
}

function reducer(state = initialState, action) {
    switch (action.type) {
        case types.CHANGE_BANNERS:
            return {
                ...state,
                banners: action.banners
            }
        case types.CHANGE_RECOMMENDS:
            return {
                ...state,
                recommends: action.recommends
            }
        default:
            return state;
    }
}

export default reducer;

counter/action.js

import * as actionTypes from './const'

export const addNumberAction = (number) => ({
    type: actionTypes.ADD_NUMBER,
    number
})

export const subNumberAction = (number) => ({
    type: actionTypes.SUB_NUMBER,
    number
})

counter/const.js

export const ADD_NUMBER = 'addNumber';
export const SUB_NUMBER = 'subNumber';

counter/index.js

import reducer from './reducer'

export * from './action';

export default reducer;

counter/reducer.js

import * as types from './const';

const initialState = {
    count: 100
}

function reducer(state = initialState, action) {
    switch (action.type) {
        case types.ADD_NUMBER:
            return {
                ...state,
                count: state.count + action.number
            }
        case types.SUB_NUMBER:
            return {
                ...state,
                count: state.count - action.number
            }
        default:
            return state;
    }
}

export default reducer;

@reduxjs/toolkit

1. 使用案例

$ pnpm install @reduxjs/toolkit react-redux
├── package.json
├── pnpm-lock.yaml
├── public
│   ├── favicon.ico
│   └── index.html
└── src
    ├── App.js
    ├── index.js
    └── store
        ├── index.js
        └── user
            └── index.js

App.js

  1. 函数式组件中使用了useDispatch, useSelector 替代了以前的 connect 写法
  2. useSelector第二个参数shallowEqual可以进行浅层比较,避免别的组件操作state状态发生改变导致组件重新渲染
import {memo, useEffect} from 'react'
import {shallowEqual, useDispatch, useSelector} from 'react-redux'
import {fetchGoodsInfoAction} from "@store/modules/home"

function Home() {
    const dispatch = useDispatch()
    const state = useSelector((state) => ({
        goodsInfo: state.home.goodsInfo
    }), shallowEqual)
    useEffect(() => {
        dispatch(fetchGoodsInfoAction())
    }, [dispatch])

    return (
        <div>
            <h1>Home</h1>
            <div>{state.goodsInfo.title}</div>
            <ul>
                {
                    state.goodsInfo.list?.map((item) => (
                        <li key={item.id}>{item.image_url}</li>
                    ))
                }
            </ul>
        </div>
    )
}

export default memo(Home)

index.js

  1. 仍然需要与 react-redux Provider 结合来使用
import React, {Suspense} from 'react';
import ReactDOM from 'react-dom/client';
import 'normalize.css';
import {HashRouter} from 'react-router-dom';
import {Provider} from 'react-redux';

import store from './store'
import App from '@/App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <Provider store={store}>
            <Suspense callback={"loading"}>
                <HashRouter>
                    <App/>
                </HashRouter>
            </Suspense>
        </Provider>
    </React.StrictMode>
);

store/index.js

  1. configureStore替代了createStore的写法
  2. home: homeReducer中的home是store模块的别名
import {configureStore} from '@reduxjs/toolkit';

import homeReducer from '@store/modules/home';

const store = configureStore({
    reducer: {
        home: homeReducer
    }
})

export default store;

store/modules/index.js

  1. createSlice 来创建一个函数片段
  2. createAsyncThunk 来编写网络请求代码
  3. reducers中的方法放置在了userSlice.actions
  4. export default userSlice.reducer返回给store进行配置
import {createSlice, createAsyncThunk} from '@reduxjs/toolkit';

import {getGoodsInfo} from '@service/modules/home';

export const fetchGoodsInfoAction = createAsyncThunk("GoodsInfo",async () => {
    return await getGoodsInfo()
})

const homeSlice = createSlice({
    name: 'home',
    initialState: {
        goodsInfo: {}
    },
    reducers: {
        changeGoodsInfo(state, action) {
            state.goodsInfo = action.payload;
        }
    },
    extraReducers: {
        [fetchGoodsInfoAction.fulfilled]: (state, action) => {
            console.log(action.payload);
            state.goodsInfo = action.payload;
        }
    }
})

export const {changeGoodsInfo} = homeSlice.actions;

export default homeSlice.reducer;
05-17 15:47