reduxjs / redux-toolkit

The official, opinionated, batteries-included toolset for efficient Redux development
https://redux-toolkit.js.org
MIT License
10.71k stars 1.17k forks source link

Allowing setting a default signature for actions created in a slice. #729

Open lebobbi opened 4 years ago

lebobbi commented 4 years ago

The current implementation of the createSlice by default creates an actionCreator with the signature for a single parameter. In our application we are using the meta pretty much on every single action and sometimes the error properties of the flux standard actions as well. And while redux toolkit has been great to reduce boilerplate, having to override the action with prepare for every single case kind of fails this purpose. Is there a way we could add an extra property to the slice configuration where we could define the default signature for that slice generated action creators?

I was thinking something like this:

// An ordered array with name of params.
const defaultActionCreator = ['payload', 'meta', 'error'];
// An ordered array of tuples with name and default value of param.
const defaultActionCreator = [['payload', {}], ['meta', {}], ['error', false]]

And in the implementation would be something like this:

const slice = function createSlice({
    name: "someSlice",
    initialState: {},
    reducers: {
      getAll(state, action){},
      ...
    },
    defaultActionCreator: ['payload', ['meta', {}]],
})

Then the signature of the 'getAll' actionCreator would end up like this.

// Generated action creator
getAll(payload, meta = {});
phryneas commented 4 years ago

This would have pretty heavy consequences in our types, which are already too complicated and are just bound to get more complicated due to #637.

So for now, I'm not really seeing this make it into RTK, but with #637, you will be able to do things like

createSlice({
  name,
  initialState,
  reducers: create => ({ // callback form
    addTodo: create.preparedReducer(
      (text: string) => ({payload: {text, id: nanoid()}}),
      (state, action) => void state.push(action.payload);
    )
  })
})

so from that point on you could have a method like

function withPayloadAndMeta<P,M>(){
  return function prepare(payload: P, meta: M) { return {payload, meta}; }
}

and use it like

createSlice({
  name,
  initialState,
  reducers: create => ({
    addTodo: create.preparedReducer(
      withPayloadAndMeta<string, number>(),
      (state, action) => void state.push(action.payload);
    )
  })
})
EskiMojo14 commented 3 months ago

this would be a good use case for custom reducer creators (#3837):

const enhancedReducerCreator = {
  type: enhancedReducerCreatorType,
  create(reducer) {
    return preparedReducerCreator.create(withPayloadAndMeta(), reducer)
  }
}

createSlice({
  name,
  initialState,
  creators: { enhancedReducer: enhancedReducerCreator },
  reducers: (create) => ({
    addTodo: create.enhancedReducer<string, number>(
      (state, action) => void state.push(action.payload)
    )
  }),
})