Search code examples
javascriptreduxredux-toolkitredux-thunk

How redux thunk payloadCreator callback function without parameters should be handled?


My app needs to fetch settings in startup. It doesn't need any parameters because it fetches everything. There haven't been issue before adding

rejectWithValue

so that actual status or error can be shown to users. However it needs 2 parameters so I did it like this:

export const fetchClientSettings = createAsyncThunk(
  'app-settings/fetch-client-settings', 
  async (_notUsed = undefined, { rejectWithValue }) => {
    try {
      const response = await fetch(API_URL);
      if (!response.ok) {
        return rejectWithValue(new Error(response.statusText));
      }
      return await response.json();
    } catch (error) {
      return rejectWithValue(error);
    }
  }
);

I'd like to know how this is done the correct way. For some reason the documentation doesn't mention this case. Only how to handle more than 1 parameters.


Solution

  • For some reason the documentation doesn't mention this case. Only how to handle more than 1 parameters.

    That is because this is more a Javascript "thing" than it is a Redux & Redux-Toolkit "thing". There's a fair amount of assumption that developers have familiarity with the language.

    Redux-Toolkit created actions always take up to a single argument, e.g. the action payload. createAsyncThunk actions are no different in this regard. The payload creators are passed two arguments, the value that is to be the action payload, and the thunkApi that allows the asynchronous action access to the store instance and various utilities.

    If you didn't pass any argument, and didn't need to access the thunkAPI in the second arg, the code could simply look like the following:

    export const myAsyncAction = createAsyncThunk(
      'async/myAsyncAction', 
      async () => {
        ...
        return /* something */;
      }
    );
    
    dispatch(myAsyncAction());
    

    In Javascript if a function takes multiple arguments that don't necessarily all need to have defined values, they must still be declared in the function declaration. Any valid Javascript identifier will work here, but a common practice is to use the underscore "_" character for these unused function parameters/arguments. You could also use it with a more descriptive name if there was more than 1 unused argument, e.g. "_unused_arg_1". In other words, whatever works for you or your team is fine. If you are not passing a value for the payload, you still need to declare an identifier so you can "skip" it to get to the second argument that you do need to access.

    export const myAsyncAction = createAsyncThunk(
      'async/myAsyncAction', 
      async (_, thunkApi) => {
        try {
          // ... happy path logic
          return data;
        } catch (error) {
          // ... sad path logic
          return thunkApi.rejectWithValue(error);
        }
      }
    );
    
    dispatch(myAsyncAction());
    

    Just for information, if you needed to pass more than 1 argument to an createAsyncThunk action you'd pack it into a single object as the first argument to the payload.

    Example:

    export const myAsyncAction = createAsyncThunk(
      'async/myAsyncAction', 
      async (arg, thunkApi) => {
        const { foo, bar } = arg;
        try {
          // ... happy path logic
          return data;
        } catch (error) {
          // ... sad path logic
          return thunkApi.rejectWithValue(error);
        }
      }
    );
    
    dispatch(myAsyncAction({ foo: "foo", bar: "bar" }));