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.
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" }));