Динамическая загрузка 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отображается по умолчанию. Единственная проблема с ним заключается в том, что он выполняет createSlice2 раза. Но поскольку это происходит только при запуске приложения, я не вижу проблем с производительностью при таком подходе. Возможно, шаблон будет конфликтовать с расширенным использованием, поэтому, если кто-то увидит узкое место, мы можем обсудить его и выяснить, как его улучшить.

Другие вопросы по тегам