Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom reducer creators #3837

Open
EskiMojo14 opened this issue Oct 31, 2023 · 1 comment
Open

Custom reducer creators #3837

EskiMojo14 opened this issue Oct 31, 2023 · 1 comment
Labels
enhancement New feature or request
Milestone

Comments

@EskiMojo14
Copy link
Collaborator

EskiMojo14 commented Oct 31, 2023

courtesy of @phryneas

With the addition of the create callback syntax for createSlice in 2.0, it could be useful to allow custom builders of some fashion to be passed to createSlice (or a function that makes a createSlice function)

creators would need to return a definition which is built immediately when createSlice is called, and then handle that definition by adding any action creators, case reducers, and matchers necessary.

for example, the new create.asyncThunk creator:

  • creates an async thunk
  • attaches the async thunk to .actions
  • adds mappings from any provided pending/fulfilled/rejected case reducers to the corresponding action type
  • adds a matcher if a settled case reducer is provided
  • attaches its case reducers to .caseReducers

type-wise, because the state type is dependent on the individual createSlice call, the only way i could see it working would be with a module augmentation based system like buildCreateApi uses.

this would also be a potential solution for createSlice always pulling in createAsyncThunk for the create.asyncThunk builder - though we are investigating simpler solutions for 2.0.

@EskiMojo14 EskiMojo14 added the enhancement New feature or request label Oct 31, 2023
@EskiMojo14 EskiMojo14 added this to the Post 2.0 milestone Oct 31, 2023
@EskiMojo14
Copy link
Collaborator Author

EskiMojo14 commented Oct 31, 2023

General gist of what a module system for builders would have to look like:

type KeysWithReducerDefinition<
  CaseReducers extends SliceCaseReducers<any>,
  Type extends ReducerType
> = KeysMatching<CaseReducers, ReducerDefinition<Type>>

interface SliceReducerCreators<
  State = any,
  CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
  Name extends string = string
> {
  reducer: {
    create(
      caseReducer: CaseReducer<State, PayloadAction>
    ): CaseReducerDefinition<State, PayloadAction>
    create<Payload>(
      caseReducer: CaseReducer<State, PayloadAction<Payload>>
    ): CaseReducerDefinition<State, PayloadAction<Payload>>

    actions: {
      [Type in KeysWithReducerDefinition<
        CaseReducers,
        ReducerType.reducer
      >]: CaseReducers[Type] extends CaseReducerDefinition<State, any>
        ? ActionCreatorForCaseReducer<
            CaseReducers[Type],
            SliceActionType<Name, Type>
          >
        : never
    }

    caseReducers: {
      [Type in KeysWithReducerDefinition<
        CaseReducers,
        ReducerType.reducer
      >]: CaseReducers[Type] extends CaseReducerDefinition<State, infer A>
        ? CaseReducer<State, A>
        : never
    }
  }
  preparedReducer: {
    create<Prepare extends PrepareAction<any>>(
      prepare: Prepare,
      reducer: CaseReducer<
        State,
        ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
      >
    ): CaseReducerWithPrepareDefinition<State, Prepare>

    actions: {
      [Type in KeysWithReducerDefinition<
        CaseReducers,
        ReducerType.reducerWithPrepare
      >]: CaseReducers[Type] extends CaseReducerWithPrepareDefinition<
        State,
        infer Prepare
      >
        ? _ActionCreatorWithPreparedPayload<
            Prepare,
            SliceActionType<Name, Type>
          >
        : never
    }

    caseReducers: {
      [Type in KeysWithReducerDefinition<
        CaseReducers,
        ReducerType.reducerWithPrepare
      >]: CaseReducers[Type] extends CaseReducerWithPrepareDefinition<
        State,
        infer Prepare
      >
        ? CaseReducer<
            State,
            ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
          >
        : never
    }
  }
  asyncThunk: {
    create: AsyncThunkCreator<State>

    actions: {
      [Type in KeysWithReducerDefinition<
        CaseReducers,
        ReducerType.asyncThunk
      >]: CaseReducers[Type] extends AsyncThunkSliceReducerDefinition<
        State,
        infer ThunkArg,
        infer Returned,
        infer ThunkApiConfig
      >
        ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
        : never
    }

    caseReducers: {
      [Type in KeysWithReducerDefinition<
        CaseReducers,
        ReducerType.asyncThunk
      >]: CaseReducers[Type] extends AsyncThunkSliceReducerDefinition<
        State,
        infer ThunkArg,
        infer Returned,
        infer ThunkApiConfig
      >
        ? Id<
            Pick<
              Required<
                AsyncThunkSliceReducerConfig<
                  State,
                  ThunkArg,
                  Returned,
                  ThunkApiConfig
                >
              >,
              'fulfilled' | 'pending' | 'rejected' | 'settled'
            >
          >
        : never
    }
  }
}

type Creators<State> = {
  [Name in keyof SliceReducerCreators]: SliceReducerCreators<State>[Name]['create']
}

type CounterCases = {
  increment: CaseReducerDefinition<number, PayloadAction>
  decrementBy: CaseReducerWithPrepareDefinition<
    number,
    (amount: number) => { payload: number }
  >
  fetchAmount: AsyncThunkSliceReducerDefinition<number, void>
}

type Actions = Id<
  UnionToIntersection<
    SliceReducerCreators<number, CounterCases, 'counter'>[
      | 'asyncThunk'
      | 'reducer'
      | 'preparedReducer']['actions']
  >
>

type CaseReducers = Id<
  UnionToIntersection<
    SliceReducerCreators<number, CounterCases, 'counter'>[
      | 'asyncThunk'
      | 'reducer'
      | 'preparedReducer']['caseReducers']
  >
>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant