Testing reducer with createAsyncThunk rejected actions - redux

Currently, I am using v1.4.0 of #redux/toolkit and when I am testing a reducer using a rejected action created through a createAsync thunk with a custom rejectedWithValue. The action object can be created with a custom error payload like
asyncThunkAction.rejected(null, '', undefined, {
message: 'error',
errorCode: 500,
});
with v1.5.0 you are no longer able to pass in the 4th error argument.
How are you able to test the behaviour of the slice with the rejected action containing the rejectedWith payload?

You can now just pass it in as the first argument.
Please note that this is not a publicly documented API (the action creators are on there only for matching and accessing the error type) and we do not give guarantees for it's stability.

Related

Redux CreateAsyncThunk fetching data

I'm currently exploring the new Redux Toolkit setup. However, I stumbled upon CreateAsyncThunk when fetching data, but I cannot find pros and cons using this over a normal service class using e.g. axios.
Can someone explain to me why I should/shouldn't use the CreateAsyncThunk to fetch data from an api?
Redux is the place where you store your data.
So after your request you need to dispatch an action, to get your data into Redux.
Now, often your components are also interested in the question if you already are running a request.
That information could be stored in your component, but then other components won't know. So you store it in Redux, too.
Now you need to also dispatch an action to signal Redux that you started loading data.
The same goes for errors. You need to dispatch an action to signal Redux that loading failed.
createAsyncThunk takes code from you and executes that. But before, it dispatches an action. And depending if your code was a succcess or had an exception, it also dispatches either a "fulfilled" or "rejected" action for you.
So you only have to concentrate on the code you want to write.
So nothing stops you from doing
const myThunk = createAsyncThunk('myThunk', myApiService.getSomeData)
in the end - which would be using your api service, coupled with createAsyncThunk for the lifecycle actions - assuming your api service only takes one argument, otherwise it'd be
const myThunk = createAsyncThunk('myThunk', ({name, password}) => myApiService.getSomeData(name, password))
since cAT only forwards one argument from the action creator call.

Improper type signature for `dispatch` method despite using "app dispatch" hook

Introduction
I'm using Redux Toolkit to add Redux support to a React application served by a Django app. We're using Typescript, and so we're following the Typescript Quick Start from the Redux Toolkit docs.
An important element of using Redux (Toolkit?) with Typescript is to ensure that your dispatch(...) method has the correct type signature. As described in the docs, if you simply use the useDispatch(...) hook from react-redux,
the default Dispatch type does not know about thunks. In order to correctly dispatch thunks, you need to use the specific customized AppDispatch type from the store that includes the thunk middleware types, and use that with useDispatch. Adding a pre-typed useDispatch hook keeps you from forgetting to import AppDispatch where it's needed.
That's fine, and I followed those instructions. My code exports a new hook, just like the tutorial:
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();
My problem is: when I use that hook, the type signature for the dispatch(...) method still doesn't accept thunks. The following code:
const dispatch = useAppDispatch();
dispatch(customAction({ ... params ... }));
Produces a compilation error:
ERROR in [at-loader] ./src/components/Widget.tsx:45:9
TS2345: Argument of type 'AsyncThunkAction<void, CustomActionParams, {}>' is not assignable to parameter of type 'AnyAction'.
Property 'type' is missing in type 'AsyncThunkAction<void, CustomActionParams, {}>' but required in type 'AnyAction'.
My question: Why isn't the typed dispatch method correctly accepting AsyncThunkActions?
Things I've Tried
Interestingly, calling dispatch(...) with the AppDispatch type explicitly parameterized results in the same error. Only parameterizing dispatch(...) with any silences the error, which obviously isn't ideal.
const dispatch = useAppDispatch();
dispatch<AppDispatch>(customAction({ ... params ... })); // same error
dispatch<any>(customAction({ ... params ... })); // error silenced
This makes me think that the issue is AppDispatch not correctly inferring that it should take AsyncThunkAction types, which should happen automatically. Something that could be causing this is that my async action is defined within extraReducers, like so:
export const customAction = createAsyncThunk(
"slice/customAction",
async (payload: CustomActionParams) { ... }
);
export const slice = createSlice({
name: "slice",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(customAction.fulfilled, (state, action) => { ... });
}
});
I'm not sure why this wouldn't cause the type to update, but it's something I've got to resolve.
Differences from standard setup
A few things about my environment could also theoretically contribute to this issue, so I'm including them here for correctness:
This React application is not a single page application, but is embedded in a rendered Django template by using django-react-templatetags. This library automatically injects script tags inline to pass component props and initialize components using ReactDOM. See this page for an example on how this works.
This means that each component has its own <Provider> component, instead of one <Provider> at the root, but all share the same store.
The useAppDispatch(...) custom method is defined in my store file (store.ts) rather than a hooks.ts at the root, but that shouldn't cause any issues.
Edit: All of this is in a Yarn 2 Workspace, rather than the root directory of a package.
Questions that are not duplicates:
This question uses an incorrect definition for useAppDispatch(...). I'm using the correct definition in my code.
This question doesn't use a custom dispatch hook.
This question uses incorrect and overspecified types for the dispatch method of the thunkAPI object in the payloadCreator method of createAsyncThunk, which I don't currently use. I'm trying to use dispatch as imported via hook in a component, not in my payloadCreator method.
This issue was ultimately caused by some sort of version mismatch between the react-redux and #reduxjs/toolkit packages. I removed my yarn.lock and deleted my node_modules, and reinstalled from scratch, and the issue disappeared!
(Additionally, I also had to pin TypeScript at ~4.1.5 due to a separate issue installing TypeScript with Yarn 2.)

Redux Middleware, Promise or Thunk

I'm using Redux Thunk as middleware for Async call, but I have seen examples that use Redux Promise, what are the differences, between these middlewares.
Both redux-thunk & react-promise delay dispatching at the suitable time .
However there are some points of difference :
1. When it intervenes :
react-thunk intervenes when the action creator returns a function, not a literal object .
react-promise intervenes when the action creator still returns an action (literal object ) but this action must have property payload, and the value of this payload is a promise (instance of Promise).
2. Who is the last dispatcher :
redux-thunk : You are the last dispatcher, reach-thunk just gives you access to dispatch function as an argument of the function (that is returned by action creator)
redux-promise: It will takes the promise (in the payload property) and dispatch the same action type in the then block , but it replaces the payload of the action with the response (the argument of then block ).

Redux Dev Tools reporting <UNDEFINED> type

I am relatively new to Redux but I'm finding that in a few cases the devtools report an Action type of <UNDEFINED> and yet by printing to console immediately before dispatch (in the action creator) I see that the object does indeed have it's type:
Has anyone else seen this behaviour?
It just means that the action type is not serialized via JSON.stringify. Most likely you're using ES6 Symbol as type. So, JSON.stringify({ type: Symbol('BECOMES_UNDEFINED') }) === '{}'.
If you want Redux DevTools Extension to support unserializable data, set serialize parameter to true:
const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: true
}));
It will handle dates, regexes, undefined, error objects, symbols and functions.
I think you've imported the action as an function, try to call (payload) might solve your issue

Can the callback parameter in FirebaseAuthClient take a context parameter?

I see that the on method of a Firebase ref can take a context parameter that is this within the context of the callback. This is incredibly useful. I am wondering -- and hoping -- that the callback function that is provided to FirebaseAuthClient can also take a callback, but my intense scrutiny of the Examples, Getting Started and SDK documentation reveal no mention of it. I attempted to scour the minified firebase-auth-client.js but stopped with my sanity still intact.
A related question: FirebaseAuthClient does not seem to be included in the JavaScript SDK area. Is it anywhere?
[Engineer at Firebase]
The Firebase Simple Login constructor now accepts a context argument as its third argument. For example, to have your Simple Login callback invoked with myContextObj as its context:
var ref = new Firebase('https://<my-firebase>.firebaseio.com');
var simpleLogin = new FirebaseSimpleLogin(ref, function(error, user) { ... }, myContextObj);

Resources