-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
RFC: Type pinning and partial inference for reateAsyncThunks #1986
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
Comments
Hey, thank you for taking the time to write this up! My first couple thoughts just glancing at this:
|
Personally, I'd prefer to add an api like const cATwithTypes = createAsyncThunk.pinTypes<{ state: RootState, dispatch: AppDispatch }>() which could either be further refined const withStringReject = cATwithTypes .pinTypes<{ rejectValue: string }>() or implicitly be used with different fallbacks: const myThunk = cATwithTypes<string, string, { rejectValue: string }>(
'someUsage',
(arg, { getState, rejectWithValue}) => {
// ...
}
) that would keep the The "naming the returned thunk on an object" part is one I'm not particularly fond of to be honest. There is no real requirement that a thunk type prefix is PS: mind that both your and my suggestion above can in many situations introduce circular type references, since the slice depends on the thunk (and TS often assumes that the type of the slice also depends on the type of the thunk, even if that's not the case), so the type of the PPS: those circularities are hard to construct when you want to show one. Here you have an example: https://codesandbox.io/s/agitated-lumiere-pl659?file=/src/counter.ts |
Hi @markerikson,
No, it's not critical. And it's not only for convenience, it's for dryness. There is no requirement for the
Yes, it's similar but it couldn't work for |
That part can actually be fine, depending on how you structure your files. (if you have the thunk in a different file from the slice) |
Hi @phryneas,
Fairly good points.
import { createAsyncThunk as createAsyncThunk_base } from "@redux/toolkit";
import type { AsyncThunkConfig } from "./store";
export const createAsyncThunk = createAsyncThunk_base.pinTypes<AsyncThunkConfig>();
import { createAsyncThunk } from "../createAsyncThunk";
export const incrementAsync = createAsyncThunk.pinTypes<{ rejectValue: number }>()(
`${name}/incrementAsync`,
async (amount: number, { extra, rejectWithValue }) => {
const { data } = await extra.counterApi.fetchCount(amount);
if (data < 0) {
rejectWithValue(data);
}
return data;
}
); In general, I tend to favour the factory pattern because I don't like having to come up with new names. It's like do I do: import { useDispach } from "react-redux";
export const useAppDispatch = useDispatch<AppDispatch>(); or: import { useDispach as useDispatch_base } from "react-redux";
export const useDispatch = useDispatch_base<AppDispatch>(); I would rather have: import { createUseDispatch } from "react-redux";
export const { useDispatch } = createUseDispatch<AppDispatch>(); But again, that's a matter of personal preference and I perfectly understand going for a more beginner friendly approach.
I think RTK being opinionated it wouldn't be a bad thing to make it a little bit harder to use smiley in the
I don't think you are correct on this particular point.. Neither your proposal nor mine introduce any new require circle as long as we don't export the
Yes, you are 100% correct.
When I said "Just one that knows better". I meant compared to the average RTK user. I am well aware that you guys knows every tick of the book already. |
The cycle I'm hinting at is introduced by adding It doesn't happen always (as you were seeing, adding a method body can resolve that in some cases) and it did happen before, too. So it's not about having the tool per se, it's also about how we go teaching it afterwards. But adding the tool without teaching it is also quite pointless. |
Okay I get it. Now we are on the same page. |
I am actually seriously considering this for a future RTK 2.0, yeah: Being able to define (I just tried to put up a Twitter poll asking which form people use, but Twitter isn't letting me post it atm. Will try again later.) |
We implemented something akin to this in 1.9.0 and https://github.com/reduxjs/redux-toolkit/releases/tag/v1.9.1 . |
@markerikson Thanks for the update :) |
Hello @markerikson and @phryneas,
This proposal suggests that typescript users may get the
createAsyncThunks
function via a factory to improve DX and code readability.Benefits
AsyncThunkConfig
, users get acreateAsyncThunk
function specific for their store.rejectValue
,serializedErrorType
,pendingMeta
,fulfilledMeta
andrejectedMeta
.typePrefix
. (It relies on template literal types)Playground
Code sandbox link
counter_vanilla.ts
: State of the art with the regularcreateAsyncThunks
.counter.ts
: Use of the alternativecreateAsyncThuks
that is the object of this proposal.setup.ts
: WherecreateStore()
is supposed to be called.createAsyncThunks.ts
: File that export thecreateAsyncThuks
returned by our factory function.RTK-ext.ts
: Implementation of the factory function.Screen.Recording.2022-02-02.at.00.41.29.mov
Drawbacks
createStore()
is called or they will get requirecycles. They should also use
import type
to import theirAsyncThunkConfig
type.@redux/toolkit/factories
for example.()
is what unlocks partial argument inference but it will look a bit weird, especially to vanilla JavaScript users. That said, JavaScript users wouldn't have to know about this factory. They could keep using thecreateAsyncThunks
exported from@redux/toolkit
as they would see no benefit using the factory.Available implementation
createAsyncThunksFactory
is already available in redux-clean-architecture and here is an example DIFF induced by using this proposal.We started from the wrong foot on reddit I would understand if you are not interested in me contributing any further but I you are interested I can come up with a PR.
Best regards,
The text was updated successfully, but these errors were encountered: