-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
Proposal: a modern & typed way of writing Redux actions doing API requests #30270
Conversation
6e0a550
to
3b95c7c
Compare
Updated with a (much complex) signature for I also added some JSDoc to make it easier to understand what is does. |
3b95c7c
to
993bbe2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few small suggestions, but LGTMO
993bbe2
to
1400438
Compare
Having proper typing and avoiding all the async lifecycle boilerplate are huge pluses, but I'm slightly worried that splitting API requests and actions will make things more difficult to grasp, especially when those are in different functions. |
You could use inline It also allows to more easily tie together the frontend and the backend, where:
If you change one, then you change the other, and they are isolated such that they are easy to understand and update. In the longer term, I foresee us wanting to use a library to automatically generate the API functions & types, for example from an OpenAPI spec, for example using https://openapi-ts.pages.dev or https://orval.dev |
That sounds sensible. However, re-reading the changes, I have also a minor gripe with the fact that this split obscures the action's signature behind another layer of indirection. Otherwise this looks good to me. |
I think both approaches are fine and more readable than the current commit. I don't have a strong opinion on whether to inline the API request or not. I think in the absence of automatically-generated typed functions from API spec, I prefer them inlined, but this can be split if you think that will make moving to automatically-generated typed functions easier. |
1400438
to
06e9cb9
Compare
Updated with explicit arguments for the new actions |
06e9cb9
to
0074a47
Compare
This pull request has merge conflicts that must be resolved before it can be merged. |
0074a47
to
a0a233c
Compare
This pull request has resolved merge conflicts and is ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me overall!
I'm not sure how to make it more readable, but both the removal of async lifecycle boilerplate and the proper typing of the actions are huge improvements.
At the moment, writing Redux actions that perform API requests requires a lot of boilerplate and is not easily typed.
This PR proposes a new way of writing those, using the new
createDataLoadingThunk()
function.It is based on RTK's
createAsyncThunk
:object/action
pattern as it makes things more explicit.apiRequest
has been created to allow easy definition of data loading functions from the Mastodon APIapi/
directory, separate from the actions, and are fully typed (arguments + what the API returns). They no longer depend on Redux, which makes them easier to use (no need to passgetState
). I intend to remove the oneapi(getState)
function in another PR, and avoid loading the access token into the Redux stateonData
attribute tocreateDataLoadingThunk
, allowing you to both execute code after the data is fetched (for example seereblog()
in this PR) and transform the data that is returned to the reducer (seesubmitAccountNote()
which puts the data in an object)onData
(or it returnsundefined
), then the API result is the payload. This allows to do side-effects while preserving the payloaddiscardApiResults
which will make an empty payload. This can be used when you do not care about what the API returns, and will avoid serialising large actions with the whole resultskipLoading
createAsyncThunk
, 3 actions are dispatched during the lifetime of the thunk:myThunk.pending
when the thunk is first invokedmyThunk.fulfilled
when the thunk returns correctlymyThunk.rejected
when an error happens in the thunkaction.meta.params
(typed correctly), the result withaction.payload
when fulfilled, or the error withaction.error
when rejectedcreateThunk
wrapper to inject the middleware meta parameters into the action when fulfilled/rejected, because this is not supported (yet?) by RTK. I intend to open a feature request to get it done upstreamreblog()
andunreblog()
I need to test this more, and try implementing other actions to see if it can handle all the cases, but I am optimistic that it will make the code better and without much boilerplate.