Динамическая загрузка initialState с помощью createSlice в Redux Toolkit
Есть ли хорошо известный шаблон для внедрения полезной нагрузки динамического начального состояния в объект initialState Redux-Toolkit?
То есть я хотел бы сделать это -
import initialState from './initialState';
function generateSlice(payload = {}){
const postsSlice = createSlice({
name: 'posts',
initialState: {...initialState, ...payload}, /// inject data here
reducers: {...}
})
}
Например, {availableRooms: []}
является пустым массивом, если он не введен при инициализации с данными {availableRooms: [{...}]}
Однако этот шаблон не работает, b/c я хочу экспортировать действия для отправки, что-то вроде этого -
const postsSlice = createSlice({
name: 'posts',
initialState: {...initialState, ...payload},
reducers: {...}
})
export {actionName} from postsSlice.actions;
*****
import {actionName} from '../mySlice'
...
const dispatch = useDispatch();
dispatch(actionName('exampleVal'));
...
Меня сдерживает airbnb
правила линтинга, поэтому я не могу экспортировать по let -
let actions; ///Bad
function generateSlice(payload){
const postsSlice = createSlice({
name: 'posts',
initialState: {...initialState, ...payload},
reducers: {...}
})
actions = postsSlict.actions
}
export actions;
Функциональность, которая мне нужна, немного проще без использования createSlice
. Причина моего вопроса в том, что я видел во многих местах,createSlice
рекомендуется более createAction
+ createReducer
, но я не вижу простого способа представить динамические данные, которые я ищу.
Я ничего не знаю о redux-orm
но я думаю, что функциональность, которая мне нужна, похожа на этот вопрос SO
2 ответа
Вот мой текущий обходной путь, который пропускает
createSlice
все вместе.
В корневом рендере
...
const store = initStore(data);
<Provider store={store}>
<App />
</Provider>
И функция init (сокращена для краткости)
import {
configureStore,
getDefaultMiddleware,
combineReducers,
} from '@reduxjs/toolkit';
import reservationReducer from '@reservation/reducer';
import spaceHierarchyReducer from '@map/reducer';
import appStoreReducer from '@app/reducer';
let ReduxStore;
function initStore(
ssrState: Partial<RootStore> = {},
) {
if (ReduxStore) {
return ReduxStore;
}
const slices = {
reservation: reservationReducer,
spaceHierarchy: spaceHierarchyReducer,
appStore: appStoreReducer,
};
const reducer = combineReducers(slices);
const preloadedState = getInitialState(ssrState);
const store = configureStore({
reducer,
middleware,
preloadedState,
});
ReduxStore = store;
initDispatch(store);
return store;
}
В
getInitialState
, Я анализирую URL-адрес и настраиваю магазин на основе бизнес-требований, сочетая данные на стороне сервера + параметры, вводимые с помощью URL-адреса. Затем в
initDispatch
, Я призываю
store.dispatch()
для некоторой логики инициализации, основанной на введенном начальном состоянии.
Здесь использование Typescript весьма полезно, поскольку оно обеспечивает соблюдение формы данных, возвращаемых из
getInitialState
а также форма переходников.
Я нашел обходной путь с помощью Redux Tool Kit. Я новичок в Redux, потому что Context API не может повторно отображать экраны навигации React Native, поскольку они не являются частью основного дерева. Я не знаю, достаточно ли хорош мой подход, но вот что я подумал:
-
generateSlice()
не будет заполнять переменную действий, потому что в то время, когда делается экспорт для использования модулем RTK, generateSlice еще не вызывался. - Вначале модулю RTK нужна только структура и конфигурация для , но еще не объект хранилища. Только
configureStore
действительно заботиться о самом магазине. Так что при дублирующем вызове: экспорт действий с обычным начальным состоянием по умолчанию, а затем вызов его внутриgenerateSlice(initValue)
с реальным начальным значением по умолчанию, кажется, работает хорошо.
Чтобы всем было проще, привожу пример с официальным кратким туториалом по RTK https://redux-toolkit.js.org/tutorials/quick-start :
counterSlice.js
:
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
value: 0,
};
const slicer = initState =>
createSlice({
name: 'counter',
initialState: initState,
reducers: {
increment: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1;
},
decrement: state => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
const generateSlice = initState => {
return slicer(initState).reducer;
};
export const counter = slicer(initialState);
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counter.actions;
export default generateSlice;
store.js
становится функцией, переименуйте ее
getStore
если необходимо :
import { configureStore } from '@reduxjs/toolkit';
import generateCounterReducer from '../states/reducers/counter';
export const store = states => {
return configureStore({
reducer: {
counter: generateCounterReducer(states.counter),
},
});
};
App.js
или же
index.js
где вы помещаете поставщика избыточности:
<Provider
store={store({
counter: { value: 7 },
})}
>
И когда я загружаю компонент, значение
7
отображается по умолчанию. Единственная проблема с ним заключается в том, что он выполняет
createSlice
2 раза. Но поскольку это происходит только при запуске приложения, я не вижу проблем с производительностью при таком подходе. Возможно, шаблон будет конфликтовать с расширенным использованием, поэтому, если кто-то увидит узкое место, мы можем обсудить его и выяснить, как его улучшить.