简体   繁体   English

如何将 Redux Toolkit 与多个 React App 实例一起使用?

[英]How do I use Redux Toolkit with multiple React App instances?

We have written a React app using Redux via Redux Toolkit.我们已经通过 Redux Toolkit 使用 Redux 编写了一个 React 应用程序。 So far so fine.到目前为止一切顺利。 Now the React app shall be rendered into multiple different elements (each element shall get a new app instance) on the same page.现在 React 应用程序将在同一页面上呈现为多个不同的元素(每个元素应获得一个新的应用程序实例)。 The rendering part is straight forward: We just call ReactDOM.render(...) for each element.渲染部分很简单:我们只是为每个元素调用ReactDOM.render(...) The Redux part again brings some headache. Redux 部分再次让人头疼。 To create a new Redux store instance for each app instance, we call the configureStore function for each React app instance.为了为每个应用实例创建一个新的 Redux 存储实例,我们为每个 React 应用实例调用configureStore函数。 Our slices look similiar to this:我们的切片看起来与此类似:

import { createSlice } from '@reduxjs/toolkit'
import type { RootState } from '../../app/store'

// Define a type for the slice state
interface CounterState {
  value: number
}

// Define the initial state using that type
const initialState: CounterState = {
  value: 0,
}

const counterSlice = createSlice({
  name: 'counter',
  // `createSlice` will infer the state type from the `initialState` argument
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    }
  },
});

export const increment = (): AppThunk => async (
  dispatch: AppDispatch
) => {
  dispatch(indicatorsOrTopicsSlice.actions.increment());
};

export const decrement = (): AppThunk => async (
  dispatch: AppDispatch
) => {
  dispatch(indicatorsOrTopicsSlice.actions.decrement());
};

// Other code such as selectors can use the imported `RootState` type
export const selectCount = (state: RootState) => state.counter.value

export default counterSlice.reducer

Please note, that currently we create and export each slice statically and only once.请注意,目前我们静态地创建和导出每个切片,并且只导出一次。 Here comes my first question: Is this actually valid when creating multiple store instances or do we actually need to create also new slice instances for each app/store instance?这是我的第一个问题:这在创建多个商店实例时是否真的有效,还是我们实际上还需要为每个应用程序/商店实例创建新的切片实例? For the simple counter example provided, doing not so, seems to work, but as soon as we use an AsyncThunk as in the example below the whole thing breaks.对于提供的简单计数器示例,不这样做似乎可以工作,但是一旦我们像下面的示例中那样使用AsyncThunk ,整个事情就会中断。

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'

// First, create the thunk
const fetchUserById = createAsyncThunk(
  'users/fetchByIdStatus',
  async (userId, thunkAPI) => {
    const response = await userAPI.fetchById(userId)
    return response.data
  }
)

// Then, handle actions in your reducers:
const usersSlice = createSlice({
  name: 'users',
  initialState: { entities: [], isLoading: false, hasErrors: false },
  reducers: {
    // standard reducer logic, with auto-generated action types per reducer
  },
  extraReducers: (builder) => {
    builder.addCase(fetchUserById.pending, (state, action) => {
      state.isLoading = true;
    });
    builder.addCase(fetchUserById.rejected, (state, action) => {
      state.isLoading = false;
      state.hasErrors = true;
    });
    builder.addCase(fetchUserById.fulfilled, (state, action) => {
      // Add user to the state array
      state.entities.push(action.payload);
      state.isLoading = false;
      state.hasErrors = true;
    });
  },
});

I believe the breaking starts here because of interdifferences between the events fired from dispatching the AsyncThunk .我相信中断从这里开始是因为从调度AsyncThunk触发的事件之间的AsyncThunk Thereby I think the solution is to call the createAsyncThunk function for each app/store/slice instance.因此我认为解决方案是为每个应用程序/商店/切片实例调用createAsyncThunk函数。 Are there any best practices for doing so?有没有这样做的最佳实践? Of course this breaks the beauty and functionality of static exports and requires kind of a mapping, hence I'm asking.当然,这破坏了静态导出的美感和功能,并且需要某种映射,因此我在问。

My original suspicion that the AsyncThunk -part was responsible for the interferences between the stores of the different React app instances was wrong.我最初怀疑AsyncThunk部分是造成不同 React 应用程序实例的存储之间的干扰的原因是错误的。 The source was something different not visible in the examples provided in my question.来源与我的问题中提供的示例中不可见的不同。 We use memoized selectors via createSelector from reselect .我们通过reselect createSelector使用记忆化选择器。 Those were created and exported like the rest statically which in fact is a problem when working with multiple store/app instances.这些是静态创建和导出的,这实际上在使用多个商店/应用程序实例时是一个问题。 This way all instances use the same memoized selector which again doesn't work correctly thereby, since in the worst scenario the stored values of the dependency selectors are coming from the use from another store/app instance.这样,所有实例都使用相同的记忆选择器,这再次无法正常工作,因为在最坏的情况下,依赖选择器的存储值来自另一个商店/应用程序实例的使用。 This again can lead to endless rerenderings and recomputations.这又会导致无休止的重新渲染和重新计算。

The solution I came up with, is to create the memoized selectors for each app instance freshly.我想出的解决方案是为每个应用程序实例新鲜地创建记忆化的选择器。 Therefore I generate a unique id for each app instance which is stored permanently in the related Redux store.因此,我为每个应用程序实例生成一个唯一的 ID,该 ID 永久存储在相关的 Redux 商店中。 When creating the store for an app instance I create also new memoized selectors instances and store them in a object which is stored in a static dictionary using the appId as the key.在为应用程序实例创建商店时,我还创建了新的记忆选择器实例,并将它们存储在一个对象中,该对象存储在使用 appId 作为键的静态字典中。 To use the memoized selectors in our components I wrote a hook which uses React.memo :为了在我们的组件中使用记忆化选择器,我编写了一个使用React.memo的钩子:

import { useMemo } from "react";
import { useSelector } from "react-redux";
import { selectAppId } from "../redux/appIdSlice";
import { getMemoizedSelectors } from "../redux/memoizedSelectors";

// Hook for using created memoized selectors
export const useMemoizedSelectors = () => {
  const appId = useSelector(selectAppId);
  const allMemoizedSelectors = useMemo(() => {
    return getMemoizedSelectors(appId);
  }, [appId]);

  return allMemoizedSelectors;
};

Then the selectors can be used in the components like this:然后可以在组件中使用选择器,如下所示:

function MyComponent(): ReactElement {
  const {
    selectOpenTodos,
  } = useMemoizedSelectors().todos;
  const openTodos = useSelector(selectOpenTodos);
  // ...
}

and the related dictionary and lookup process would look like this:相关的字典和查找过程如下所示:

import { createTodosMemoizedSelectors } from "./todosSlice";

/**
 * We must create and store memoized selectors for each app instance on its own,
 * else they will not work correctly, because memoized value would be used for all instances.
 * This dictionary holds for each appId (the key) the related created memoized selectors.
 */
const memoizedSelectors: {
  [key: string]: ReturnType<typeof createMemoizedSelectors>;
} = {};

/**
 * Calls createMemoizedSelectors for all slices providing
 * memoizedSelectors and stores resulting selectors
 * structured by slice-name in an object.
 * @returns object with freshly created memoized selectors of all slices (providing such selectors)
 */
const createMemoizedSelectors = () => ({
  todos: createTodosMemoizedSelectors(),
});

/**
 * Creates fresh memoized selectors for given appId.
 * @param appId the id of the app the memoized selectors shall be created for
 */
export const initMemoizedSelectors = (appId: string) => {
  if (memoizedSelectors[appId]) {
    console.warn(
      `Created already memoized selectors for given appId: ${appId}`
    );
    return;
  }
  memoizedSelectors[appId] = createMemoizedSelectors();
};

/**
 * Returns created memoized selectors for given appId.
 */
export const getMemoizedSelectors = (appId: string) => {
  return memoizedSelectors[appId];
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 React 应用程序中使用 redux 工具包更新不同减速器(切片)中的嵌套项 - How do I update a nested Item in a different reducer (slice) with redux toolkit in a React app 如何在同一个组件中使用来自 redux-toolkit 的多个 Query 钩子? - How do I use multiple Query hooks from redux-toolkit in the same component? 如何在 React 中只运行一次 redux -toolkit? - How do I run redux -toolkit only once in React? 如何使用 react suspense 和 redux 工具包? - How use react suspense with redux toolkit? 如果我使用 firebase 在 React Native 中进行身份验证,我是否需要使用 redux-toolkit - Do i need to use redux-toolkit if i am using firebase to authenticate in react native 我应该如何使用 Redux 工具包中的选择器? - How should I use selectors in Redux Toolkit? (反应 redux 工具包)我想知道使用多个参数的动作 - (react redux toolkit) i want to know using action with multiple prameters 如何将 notistick/(anysnackbar) 与 redux-toolkit 一起使用并做出反应? - How can I use notistick/(any snackbar) with redux-toolkit and react? 使用 Redux 工具包,我如何从非反应文件访问商店? - Using Redux Toolkit, how do I access the store from a non-react file? 如何将 redux-toolkit createSlice 与 React class 组件一起使用 - How to use redux-toolkit createSlice with React class components
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM