从 0 开始搭建 React 框架-配置 redux

时间:2024-01-27 12:26:13

官方已推荐使用 @reduxjs/toolkit(地址:https://cn.redux.js.org/introduction/why-rtk-is-redux-today

使用传统 redux

安装 redux、react-redux、redux-thunk

yarn add redux react-redux redux-thunk
// src/store-redux/index.js
import {createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { thunk } from 'redux-thunk';
import { reducer as page1Reducer } from '@pages/page1/reducer';


const combinedReducerObj = {
    page1: combineReducers(page1Reducer)
};

const store = createStore(
    combineReducers(combinedReducerObj),
    compose(applyMiddleware(thunk)
));

export default store;
// src/App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store-redux/index';
import Page1 from '@pages/page1/index.jsx';

const App = () => {
    return <Provider store={store}>
        <Page1 />
    </Provider>;
};

export default App;
// src/pages/page1/reducer.js

// 定义 actionType
const ADD_TODO = 'ADD_TODO';
const TODO_TOGGLED = 'TODO_TOGGLED';
const SET_DATA = 'SET_DATA';

// 定义 reducer
const todos = (state = [{ id: 1, text: 'zhangsan', completed: false }], action) => {
    switch (action.type) {
        case ADD_TODO:
            return state.concat({
                id: action.payload.id,
                text: action.payload.text,
                completed: false
            });
        case TODO_TOGGLED:
            return state.map(todo => {
                if (todo.id !== action.payload.id) return todo

                return {
                    ...todo,
                    completed: !todo.completed
                }
            });
        default:
            return state
    }
};

const dataList = (state = [], action) => {
    if (action.type === SET_DATA) {
        return action.payload;
    }

    return state;
};

export const reducer = {
    todos,
    dataList
};

export const actionTypes = {
    ADD_TODO,
    TODO_TOGGLED,
    SET_DATA
};
// src/pages/page1/actions.js
import { actionTypes } from './reducer';
import { getDataService } from './service';

// 同步 action
const addTodo = ({ text }) => {
    return {
        type: actionTypes.ADD_TODO,
        payload: {
            text,
            id: Math.random()
        }
    };
};

const todoToggled = ({ id }) => {
    return {
        type: actionTypes.TODO_TOGGLED,
        payload: {
            id
        }
    };
};

const setDataList = data => {
    return {
        type: actionTypes.SET_DATA,
        payload: data
    };
};

// 异步 action
const thunkGetDataList = (data) => {
    return (dispatch, getState) => {
        // dispatch(addTodo({ text: 'lisi'}));

        const page1State = getState().page1;

        console.log(page1State);

        getDataService().then(res => {
            dispatch(setDataList(res.data))
        });
    };
};

export default {
    // 同步 action
    addTodo,
    todoToggled,

    // 异步 action
    setDataList,
    thunkGetDataList
};
// src/pages/page1/service.js
// 模拟接口获取数据
export const getDataService  = (params) => {
    console.log(params);

    return new Promise((resolve, reject) => {
        const res= {
            data: [
                {
                    id: 1,
                    name: 'zhangsan',
                    age: 20
                },
                {
                    id: 2,
                    name: 'lisi',
                    age: 30
                }
            ]
        };

        setTimeout(() => {
            resolve(res);
        }, 3000)
    });
};
// src/pages/page1/index.jsx
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import actions from './actions';

const Page1 = ({
        todos,
        dataList,

        addTodo,
        getData

}) => {
    useEffect(() => {
        getData();
    }, []);

    return <div>
        <p>This is App Component.</p>


        {
            dataList.map((item, index) => {
                return (
                    <div key={item.id}>
                        <p>id: {item.id};姓名: {item.name};年龄:{item.age}</p>
                    </div>
                );
            })
        }

        <p>-------------------------------------------</p>

        {
            todos.map((item, index) => {
                return (
                    <div key={item.id}>
                        <p>id: {item.id};text: {item.text};compoleted: {item.completed}</p>
                    </div>
                );
            })
        }

        <button onClick={addTodo}>添加</button>

    </div>;
};

const mapStateToProps = (state, props) => {
        // let stateCommon = state.common;
        const statePage1 = state.page1;

        return {
            todos: statePage1.todos,
            dataList: statePage1.dataList
        };
    },

    mapDispatchToProps = (dispatch, props) => {
        return {
            addTodo: () => {
                dispatch(actions.addTodo({ id: 4, text: 'zzz' }));
            },
            getData: () => {
                dispatch(actions.thunkGetDataList());
            }
        };
    };

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(React.memo(Page1));

使用 @reduxjs/toolkit

安装 @reduxjs/toolkit、react-redux

yarn add @reduxjs/toolkit react-redux

官方使用方案

创建 store
// src/store/index.js
import { configureStore } from '@reduxjs/toolkit';
import commonReducer from './common/reducer';
import HomeReducer from '@pages/home/reducer';

export const store = configureStore({
    // 合并所有的 reducer
    reducer: {
        common: commonReducer,
        home: HomeReducer
    },
    // 解决 redux 无法序列化 数据时的报错
    middleware: getDefaultMiddleware => getDefaultMiddleware({ serializableCheck: false })
});

export default store.dispatch;
创建 reducerSlice
  • configureStore 创建 store
  • 在根节点通过 Provider 传入 store
  • createSlice 创建 reducer 切片:传入 name、初始状态、定义 reducer,生成 reduceractions
  • 页面上使用:通过 useSlector 获取状态;useDispatch 分发 action
// src/page2/reducer.js
import { createSlice } from '@reduxjs/toolkit';

const todosSlice = createSlice({
    name: 'page2', // 命名空间
    initialState: {
        counter: 0,
        todoList: []
    }, // 初始值
    reducers: {
        counterIncrement: state => {
            // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
            // 并不是真正的改变状态值,因为它使用了 Immer 库
            // 可以检测到“草稿状态“ 的变化并且基于这些变化生产全新的
            // 不可变的状态
            state.counter += 1;
        },
        counterDecrement: state => {
            state.counter -= 1;
        },
        counterIncrementByAmount: (state, action) => {
            state.counter += action.payload;
        },
        todoListAdded(state, action) {
            state.todoList.push({
                id: action.payload.id,
                text: action.payload.text,
                completed: false
            });
        },
        todoListToggled(state, action) {
            const todoItem = state.todoList.find(todo => todo.id === action.payload);

            todoItem.completed = !todoItem.completed;
        }
    }
});

// 普通 action
export const {
    counterDecrement,
    counterIncrement,
    counterIncrementByAmount,
    todoListAdded,
    todoListToggled
} = todosSlice.actions;

export const actions = todosSlice.actions;

// reducer
export default todosSlice.reducer;
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import page2Reducer from '@pages/page2/reducer';

export const store = configureStore({
    // 传入所有的 reducer, configureStore 会帮我们自动合并 reducer
    reducer: {
        page2: page2Reducer
    }
});
// src/pages/page2/index.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { actions } from './reducer';

const Page2 = () => {
    const { counter, todoList } = useSelector(state => state.page2), // 获取状态
        dispatch = useDispatch();

    return <div>
        <p>This is App Component.</p>

        <p>计数器:{ counter }</p>

        {
            todoList.map((item, index) => {
                return (
                    <div key={item.id}>
                        <p>id: {item.id}</p>
                        <p>text: {item.text}</p>
                        <p>completed: {String(item.completed)}</p>
                    </div>
                );
            })
        }
        <button onClick={() => dispatch(actions.counterIncrement())}>计数器添加</button>
        <button onClick={() => dispatch(actions.counterDecrement())}>计数器减少</button>
        <button onClick={() => dispatch(actions.counterIncrementByAmount(5))}>计数器每次+5</button>
        <button onClick={() => dispatch(actions.todoListAdded({ id: 1, text: 'zhangsan' }))}>添加</button>
        <button onClick={() => dispatch(actions.todoListToggled(1))}>切换状态</button>
    </div>;
};

export default React.memo(Page2);
处理异步操作 createAsyncThunk

传统 redux 需要使用安装 redux-thunk
redux toolkit 使用 createAsyncThunk API 简化异步调用。

// src/pages/page2/asyncActions.js
import {  createAsyncThunk } from '@reduxjs/toolkit';
import { actions } from './reducer';

// 模拟接口获取数据
const getDataService  = (params) => {
    console.log(params);

    return new Promise((resolve, reject) => {
        const res= {
            data: [
                {
                    id: 1,
                    name: 'zhangsan',
                    age: 20
                },
                {
                    id: 2,
                    name: 'lisi',
                    age: 30
                }
            ]
        };

        setTimeout(() => {
            resolve(res);
        }, 3000)
    });
};

// 定义异步 action
export const asyncGetData = createAsyncThunk('page2/fetchPosts', async (payload, { dispatch, getState }) => {
    // 通过 payload 可以接收数据
    const { params } = payload;

    // 通过 getState 可以获取 store
    const { counter } = getState().page2;
    console.log(counter);

    // 通过 dispatch 可以分发 action
    // 在 createAsyncThunk 可以 dispatch 普通的 action
    dispatch(actions.counterIncrement());
    // 在 createAsyncThunk 也可以 dispatch 异步 action
    dispatch(asyncSetLocale({ locale: 'pl' }));

    const response = await getDataService(params);

    // 在这里 return 的数据,需要在 createSlice 的 extraReducers 里进行处理,更新 state
    return {
        dataList: response.data
    };
});

export const asyncSetLocale = createAsyncThunk('page2/setLocael', async (payload, { dispatch, getState }) => {
        return {
            locale: payload.locale,
            menuList: [{
                id: 11,
                menuName: '电站'
            }]
        };
    });


// 导出异步 actions
const asyncActions = {
    asyncGetData,
    asyncSetLocale
};

export default asyncActions;
// src/pages/page2/reducer.js
import { createSlice } from '@reduxjs/toolkit';
import asyncActions from './asyncAtions';

const todosSlice = createSlice({
    name: 'page2', // 命名空间
    initialState: {
        counter: 0,
        todoList: [],
        dataList: [], // 接口返回的数据
        locale: 'en',
        menuList: []
    }, // 初始值
    reducers: {
        counterIncrement: state => {
            // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
            // 并不是真正的改变状态值,因为它使用了 Immer 库
            // 可以检测到“草稿状态“ 的变化并且基于这些变化生产全新的
            // 不可变的状态
            state.counter += 1;
        },
        counterDecrement: state => {
            state.counter -= 1;
        },
    },
    extraReducers: builder => {
        builder.addCase(asyncActions.asyncGetData.fulfilled, (state, action) => {
            // 接收 createAsyncThunk 中 return 的数据
            const { dataList } = action.payload;

            // 设置 state
            state.dataList = dataList;
        });

        builder.addCase(asyncActions.asyncSetLocale.fulfilled, (state, action) => {
            const {
                locale,
                menuList
            } = action.payload;

            state.locale = locale;
            state.menuList = menuList;
        });
    }
});

// 普通 action
export const {
    counterDecrement,
    counterIncrement,
} = todosSlice.actions;

export const actions = todosSlice.actions;

// reducer
export default todosSlice.reducer;
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import asyncActions from './asyncAtions';

const Home = () => {
    const { counter, todoList, locale, menuList, dataList } = useSelector(state => state.page2), // 获取状态
        dispatch = useDispatch();

    useEffect(() => {
    	// 调用异步 action
        dispatch(asyncActions.asyncGetData({ query: '参数1' }));
    }, []);

    return <div>
        <p>This is App Component.</p>

        {/* <p>计数器:{ counter }</p> */}
        <p>当前语言: { locale }</p>

        {
            dataList.map((item, index) => {
                return (
                    <div key={item.id}>
                        <p>id: {item.id};姓名: {item.name};年龄:{item.age}</p>
                    </div>
                );
            })
        }

        <p>-------------------------------------------</p>

        {
            menuList.map((item, index) => {
                return (
                    <div key={item.id}>
                        <p>id: {item.id};菜单: {item.menuName}</p>
                    </div>
                );
            })
        }
    </div>;
};

export default React.memo(Home);

简化 reducerSlice 和 createAsyncThunk

简化 createAsyncThunk

由于 createAsyncThunk 内部就可以直接 dispatch 一个普通的 action,就可以直接在这里面通过 dispatch(actions.setXxx('zhangsan')) 的方式修改 state,不需要 return 数据,这样就不用在 createSlice 中编写 extraReducers

// src/pages/page2/asyncActions.js
import {  createAsyncThunk } from '@reduxjs/toolkit';
import { actions } from './reducer';

// 模拟接口获取数据
const getDataService  = (params) => {
    console.log(params);

    return new Promise((resolve, reject) => {
        const res= {
            data: [
                {
                    id: 1,
                    name: 'zhangsan',
                    age: 20
                },
                {
                    id: 2,
                    name: 'lisi',
                    age: 30
                }
            ]
        };

        setTimeout(() => {
            resolve(res);
        }, 3000)
    });
};

// 定义异步 action
export const asyncGetData = createAsyncThunk('page2/fetchPosts', async (payload, { dispatch, getState }) => {
    // 通过 payload 可以接收数据
    const { params } = payload;

    // 通过 getState 可以获取 store
    const { counter } = getState().page2;
    console.log(counter);

    // 通过 dispatch 可以分发 action
    // 在 createAsyncThunk 可以 dispatch 普通的 action
    dispatch(actions.counterIncrement());
    // 在 createAsyncThunk 也可以 dispatch 异步 action
    dispatch(asyncSetLocale({ locale: 'pl' }));

    const response = await getDataService(params);

    // 在这里 return 的数据,需要在 createSlice 的 extraReducers 里进行处理,更新 state
    // return {
    //     dataList: response.data
    // };
    // 直接在这里 setState
    dispatch(actions.setDataList(response.data));
});

export const asyncSetLocale = createAsyncThunk('page2/setLocael', async (payload, { dispatch, getState }) => {
        // return {
        //     locale: payload.locale,
        //     menuList: [{
        //         id: 11,
        //         menuName: '电站'
        //     }]
        // };
        // 直接在这里 setState
        dispatch(actions.setLocale(payload.locale));
        dispatch(actions.setMenuList([{ id: 11, menuName: '电站' }]));
    });


// 导出异步 actions
const asyncActions = {
    asyncGetData,
    asyncSetLocale
};

export default asyncActions;
// src/pages/page2/reducer.js
import { createSlice } from '@reduxjs/toolkit';
import asyncActions from './asyncAtions';

const todosSlice = createSlice({
    name: 'page2', // 命名空间
    initialState: {
        counter: 0,
        todoList: [],
        dataList: [], // 接口返回的数据
        locale: 'en',
        menuList: []
    }, // 初始值
    reducers: {
        counterIncrement: state => {
            // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
            // 并不是真正的改变状态值,因为它使用了 Immer 库
            // 可以检测到“草稿状态“ 的变化并且基于这些变化生产全新的
            // 不可变的状态
            state.counter += 1;
        },
        counterDecrement: state => {
            state.counter -= 1;
        },
        setDataList(state, action) {
            state.dataList = action.payload;
        },
        setLocale(state, action) {
            state.locale = action.payload;
        },
        setMenuList(state, action) {
            state.menuList = action.payload;
        }
    },
    // extraReducers: builder => {
    //     builder.addCase(asyncActions.asyncGetData.fulfilled, (state, action) => {
    //         // 接收 createAsyncThunk 中 return 的数据
    //         const { dataList } = action.payload;
    //
    //         // 设置 state
    //         state.dataList = dataList;
    //     });
    //
    //     builder.addCase(asyncActions.asyncSetLocale.fulfilled, (state, action) => {
    //         const {
    //             locale,
    //             menuList
    //         } = action.payload;
    //
    //         state.locale = locale;
    //         state.menuList = menuList;
    //     });
    // }
});

export const actions = todosSlice.actions;

// reducer
export default todosSlice.reducer;
// src/pages/page2/index.jsx
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import asyncActions from './asyncAtions';

const Home = () => {
    const { counter, todoList, locale, menuList, dataList } = useSelector(state => state.page2), // 获取状态
        dispatch = useDispatch();

    useEffect(() => {
        dispatch(asyncActions.asyncGetData({ query: '参数1' }));
    }, []);

    return <div>
        <p>This is App Component.</p>

        {/* <p>计数器:{ counter }</p> */}
        <p>当前语言: { locale }</p>

        {
            dataList.map((item, index) => {
                return (
                    <div key={item.id}>
                        <p>id: {item.id};姓名: {item.name};年龄:{item.age}</p>
                    </div>
                );
            })
        }

        <p>-------------------------------------------</p>

        {
            menuList.map((item, index) => {
                return (
                    <div key={item.id}>
                        <p>id: {item.id};菜单: {item.menuName}</p>
                    </div>
                );
            })
        }
    </div>;
};

export default React.memo(Home);

如果你还是想在 cerateAsyncThunk 中通过 return 得形式返回状态。那么可以优化 extraReducers 的写法,前提是 return 一个对象。

// src/store/createExtraReducers.js
export const createExtraReducers = (actions = {}) => {
    const extraReducers = {};

    for (const action in actions) {
        if (actions.hasOwnProperty(action)) {
            // action 的异步任务执行完成才修改 state
            extraReducers[actions[action].fulfilled] = (state, { payload }) => {
                for (const key in payload) {
                    if (Object.hasOwnProperty.call(payload, key)) {
                        if (key !== 'callback') {
                            state[key] = payload[key];
                        } else {
                            payload.callback && payload.callback();
                        }
                    }
                }
            };
        }
    }

    return extraReducers;
};
// src/pages/page2/reducer.js
import { createSlice } from '@reduxjs/toolkit';
import asyncActions from './asyncAtions';
import { createExtraReducers } from '@store/createExtraReducers.js';

const extraReducers = createExtraReducers(asyncActions);

const todosSlice = createSlice({
    name: 'page2', // 命名空间
    initialState: {
        counter: 0,
        todoList: [],
        dataList: [], // 接口返回的数据
        locale: 'en',
        menuList: []
    }, // 初始值
    reducers: {
        counterIncrement: state => {
            // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
            // 并不是真正的改变状态值,因为它使用了 Immer 库
            // 可以检测到“草稿状态“ 的变化并且基于这些变化生产全新的
            // 不可变的状态
            state.counter += 1;
        },
        counterDecrement: state => {
            state.counter -= 1;
        },
        setDataList(state, action) {
            state.dataList = action.payload;
        },
        setLocale(state, action) {
            state.locale = action.payload;
        },
        setMenuList(state, action) {
            state.menuList = action.payload;
        }
    },
     extraReducers: builder => {
	     for (const actionCase in extraReducers) {
            if (extraReducers.hasOwnProperty(actionCase)) {
               builder.addCase(actionCase, (state, action) =>
                   extraReducers[actionCase](state, action)
            	);
         	}
    	}
    }
});

export const actions = todosSlice.actions;

// reducer
export default todosSlice.reducer;
import {  createAsyncThunk } from '@reduxjs/toolkit';
import { actions } from './reducer';

// 模拟接口获取数据
const getDataService  = (params) => {
    console.log(params);

    return new Promise((resolve, reject) => {
        const res= {
            data: [
                {
                    id: 1,
                    name: 'zhangsan',
                    age: 20
                },
                {
                    id: 2,
                    name: 'lisi',
                    age: 30
                }
            ]
        };

        setTimeout(() => {
            resolve(res);
        }, 3000)
    });
};

// 定义异步 action
export const asyncGetData = createAsyncThunk('page2/fetchPosts', async (payload, { dispatch, getState }) => {
    // 通过 payload 可以接收数据
    const { params } = payload;

    // 通过 getState 可以获取 store
    const { counter } = getState().page2;
    console.log(counter);

    // 通过 dispatch 可以分发 action
    // 在 createAsyncThunk 可以 dispatch 普通的 action
    dispatch(actions.counterIncrement());
    // 在 createAsyncThunk 也可以 dispatch 异步 action
    dispatch(asyncSetLocale({ locale: 'pl' }));

    const response = await getDataService(params);

    // 在这里 return 的数据,需要在 createSlice 的 extraReducers 里进行处理,更新 state
    return {
        dataList: response.data
    };
});

export const asyncSetLocale = createAsyncThunk('page2/setLocael', async (payload, { dispatch, getState }) => {
        return {
            locale: payload.locale,
            menuList: [{
                id: 11,
                menuName: '电站'
            }]
        };
    });


// 导出异步 actions
const asyncActions = {
    asyncGetData,
    asyncSetLocale
};

export default asyncActions;
简化 createSlice

由于需要给每个 state 定义至少一个修改 state 的 reducer,为了简化这一步,其实每个 state 只需要定义一个 setState 方法就可以了,处理数据可以放在外部去处理,我们只要传递 newState 即可。

比如定一个 name,修改它直接通过 dispatch(actions.setName('zhangsan'))

这样就可以封装一个统一的方法,来帮我们自动生成 state 对应的 setState,就不需要每个模块去单独写一遍,提升开发效率。

// src/store/createReducerSlice.js
// 工具方法
import { createSlice, createAction } from '@reduxjs/toolkit';

// 为每个 state 创建一个对应的 setState 方法,
// 如果 state 是 name,那么修改 name 则通过 setName 进行修改
// dispatch(simpleActions.setName('zhangsan'))
const createSetState = initialState => {
        const reducers = {};

        for (const key in initialState) {
            if (initialState.hasOwnProperty(key)) {
                const keyName = 'set' + key.substring(0, 1).toUpperCase() + key.substring(1);

                reducers[keyName] = (state, { payload }) => {
                    state[key] = payload;
                };
            }
        }

        return reducers;
    },
    createReducerSlice = ({
        pageIdentify, // 页面 id
        initialState = {}, // 定义 state 初始值
         reducers = {}, // 可选项,自定义 reducer。其他更复杂的操作,比如对数组的添加/删除,如果不想在外部处理,那就在这里定义
        extraReducers = {} // 可选项,如果 createAsyncThunk 中 return 了需要修改的 state,那么需要传递 extraReducers,统一修改 state;
    }) => {
        const updateState = createAction('updateState'),
            reducerSlice = createSlice({
                name: pageIdentify,
                initialState: initialState,
                // 简单reducer
                reducers: {
                	// 简单 reducer: 一次只能修改一个状态
                    ...createSetState(initialState),
                    // 其他更复杂的操作,比如对数组的添加/删除,如果不想在外部处理,那就在这里定义
                    ...reducers
                },
                extraReducers: builder => {
                    // 复杂 reducer 一次修改多个 state
                    builder.addCase(updateState, (state, { payload }) => {
                        for (const stateKey in payload) {
                            if (payload.hasOwnProperty(stateKey)) {
                                state[stateKey] = payload[stateKey];
                            }
                        }
                    });

                    for (const actionCase in extraReducers) {
                        if (extraReducers.hasOwnProperty(actionCase)) {
                            builder.addCase(actionCase, (state, action) =>
                                extraReducers[actionCase](state, action)
                            );
                        }
                    }
                }
            });

        // 自定义 caseReducer:通过 dispatch(caseReducer.updateState({ name: 'zhangsan', age: 20 })) 可一次修改多个 state
        reducerSlice.caseReducer = {
            updateState
        };

        return reducerSlice;
    };

export default createReducerSlice;
// src/pages/page2/asyncActions.js
import {  createAsyncThunk } from '@reduxjs/toolkit';
import { actions } from './reducer';

// 模拟接口获取数据
const getDataService  = (params) => {
    console.log(params);

    return new Promise((resolve, reject) => {
        const res= {
            data: [
                {
                    id: 1,
                    name: 'zhangsan',
                    age: 20
                },
                {
                    id: 2,
                    name: 'lisi',
                    age: 30
                }
            ]
        };

        setTimeout(() => {
            resolve(res);
        }, 3000)
    });
};

// 定义异步 action
export const asyncGetData = createAsyncThunk('page2/fetchPosts', async (payload, { dispatch, getState }) => {
    // 通过 payload 可以接收数据
    const { params } = payload;

    dispatch(asyncSetLocale({ locale: 'pl' }));

    const response = await getDataService(params);

    dispatch(actions.setDataList(response.data));
});

export const asyncSetLocale = createAsyncThunk('page2/setLocael', async (payload, { dispatch, getState }) => {
        dispatch(actions.setLocale(payload.locale));
        dispatch(actions.setMenuList([{ id: 11, menuName: '电站' }]));
    });


// 导出异步 actions
const asyncActions = {
    asyncGetData,
    asyncSetLocale
};

export default asyncActions;
// src/pages/page2/reducer.js
// import asyncActions from './asyncAtions';
// import { createExtraReducers } from '@store/createExtraReducers.js';
import createReducerSlice  from '@store/createReducerSlice.js';

// const extraReducers = createExtraReducers(asyncActions);

// 使用二次封装的 createReducerSlice
const reducerSlice = createReducerSlice({
    pageIdentify: 'page2',
    initialState: {
        counter: 0,
        todoList: [],
        dataList: [], // 接口返回的数据
        locale: 'en',
        menuList: []
    }, // 初始值
    // extraReducers
});

// 普通 actions
export const actions = reducerSlice.actions;
export const caseReducer = reducerSlice.caseReducer;

// reducer
export default reducerSlice.reducer;