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

Using createAction, how would one go about validating the payload passed in? #146

Closed
rmisio opened this issue May 24, 2019 · 5 comments
Closed

Comments

@rmisio
Copy link

rmisio commented May 24, 2019

Let's say I'd like to ensure some required properties are passed into the action creator. How would I do that?

I considered a wrapper function, but then the toString is not on the wrapper and would have to be carried over.

export const activateConvo = peerId => {
  if (typeof peerId !== 'string' || !peerId) {
    throw new Error('Please provide a peerId as a non-empty string.');
  }

  return createAction('CHAT_CONVO_ACTIVATE')(peerId);
}

Forgive me if this is more of a StackOverflow type question... but not really seeing any redux-starter-kit talk there.

@markerikson
Copy link
Collaborator

That is kind of the tradeoff atm, yeah. If you are using createAction() (whether directly or via createSlice()), then you are basically saying you're fine with its standard way of doing things.

You can always still write action creators by hand if you want to. The point of the functions from RSK is to provide an opinionated, obvious, no-boilerplate way of doing things.

That said, I'm still considering the idea of a "payload customization" callback somehow - I just haven't had time to think about how that might fit in.

@CGamesPlay
Copy link

CGamesPlay commented Jun 2, 2019

I'd like to vote for this issue as well. My proposal would be a payload transformer:

createAction('CHAT_CONVO_ACTIVATE', (peerId) => {
  if (typeof peerId !== 'string' || !peerId) {
    throw new Error('Please provider a peerId as a non-empty string.');
  }
  return peerId;
});

In this case (from the original issue comment), the transformer is the identity function with some validation. In other use cases (like the one I have in mind), one could change it into a different value.

@markerikson
Copy link
Collaborator

@CGamesPlay : yeah, that's basically the kind of thing I've been picturing. The biggest issue is how to make use of that with createReducer and createSlice.

@CGamesPlay
Copy link

I'm using this in my project that doesn't use createReducer (note: I haven't thought about the mapper === undefined case, the types are likely a bit weird there).

export interface ActionCreator<T extends string, P, A extends any[]> {
  (...args: A): PayloadAction<P, T>;
  type: T;
  matches(a: Action<T>): a is PayloadAction<P, T>;
}

export const createAction = <T extends string, A extends any[], P>(
  type: T,
  payload: (...args: A) => P = undefined,
): ActionCreator<T, P, A> => {
  const creator: any = payload
    ? (...args: A) => ({ type, payload: payload(...args) })
    : () => ({ type });
  creator.toString = () => name;
  creator.type = type;
  creator.matches = a => a && a.type === type;
  return creator;
};

// Typed action payloads in my custom reducers:

function customReducer(state, action) {
  if (activateConvo.matches(action)) {
    // action.payload is number
  }
}

Is the concern that TypeScript won't automatically know the action type in your createReducer reducer? Getting TypeScript to automatically know that might not be possible with the current API, but perhaps you can create a utility type that extracts the correct type? Something like this:

createReducer(0, {
  [increment.type]: (state, action: ActionType<increment>) => state + action.payload,
});

@markerikson
Copy link
Collaborator

Fixed by #149 and out as 0.6.3 . Please let me know if that works for you!

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

No branches or pull requests

3 participants