-
Notifications
You must be signed in to change notification settings - Fork 53
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
Move to Node-style callbacks in every subscription #229
Conversation
This is needed to pass errors with subscriptions, the same way we do it for the async functions by throwing errors. Contains the following changes: - Add the add SubscriptionCallback type. - Add types to all the subscriptions methods. - Move all the subscription methods to use node-style callbacks. - Update documentation and examples.
Codecov Report
@@ Coverage Diff @@
## master #229 +/- ##
==========================================
- Coverage 34.93% 34.43% -0.50%
==========================================
Files 98 99 +1
Lines 1792 1838 +46
Branches 274 295 +21
==========================================
+ Hits 626 633 +7
- Misses 1166 1205 +39
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
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.
Looking good!
I'm not super convinced by the order of the parameters. Now that I have a better understanding of the pattern Error-first callbacks I agree with the decisions.
Merge this one to make #233 easier to review. |
const apps = await parseApps(result, organization) | ||
return apps[0] | ||
} | ||
(result) => parseApp(result, organization) |
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.
@0xGabi Wondering if you tested this? Because the query is ORGANIZATION_APPS
with a limit of 1
since we added the filters (the single app query was only working by address).
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.
Apology, I didn't check on my local the change as it was small and seems straight forward but I got confused with how we were handling APP_BY_ADDRESS
query.
This commit contains various improvements for the React API. ## Loading behavior Since we replaced the GraphQL subscriptions by queries with polling (see #203), the hooks provided by the React API were updating too often, and incorrectly (see #216). This version is now taking care of only triggering updates when a changed happened, and to only use loading states when necessary. ## createAppHook(): allow connector configuration to be set App connectors can receive a source connector and its options, but this wasn’t possible when using `createAppHook()`. A second parameter has been added, to either pass a specific connector or to change its options: import connectVoting from '@aragon/connect-voting' import { createAppHook } from '@aragon/connect-react' const useVoting = createAppHook(connectVoting, ['thegraph', { subgraphUrl: 'https://…' }]) ## createAppHook(): dependencies array Hooks created through `createAppHook()` now accept a dependency array, that can get used to refresh its callback. When no dependency array is passed, the callback never updates. import connect from '@aragon/connect' import connectVoting from '@aragon/connect-voting' import { createAppHook, useApp } from '@aragon/connect-react' const useVoting = createAppHook(connectVoting) function App() { const [page, setPage] = useState(0) const [voting] = useApp('voting') const [votes] = useVoting(voting, app => ( app.votes({ first: 10, skip: 10 * page }) ), [page]) return ( <div> <button onClick={() => setPage(page + 1)}>prev</button> <button onClick={() => setPage(page - 1)}>next</button> </div> ) } ## Populating status.error rather than throwing errors In many cases, errors were thrown rather than populating the `status.error` object in the React API. It is an issue that was also happening with the JS API (see #229). It has now been fixed. import connect from '@aragon/connect' import connectVoting from '@aragon/connect-voting' import { createAppHook, useApp } from '@aragon/connect-react' const useVoting = createAppHook(connectVoting) function App() { const [voting, votingStatus] = useApp('voting') const [votes, votesStatus] = useVoting(voting, app => app.votes()) if (votingStatus.error) { return ( <h1>Error: {votingStatus.error.message}</h1> ) } if (votesStatus.error) { return ( <h1>Error: {votesStatus.error.message}</h1> ) } return ( // … ) } ## App methods subscriptions Prior to this version, it was not possible to subscribe to app methods using `createAppHook()`. The fetched data was returned once, and only updated when the component was mounted again. The feature has been added, and we can now subscribe to any app methods, alongside the core ones (`useApp()`, `usePermissions()`, …). Every part of the React API is now listening to changes and reacting accordingly. To use this feature, the subscription methods (`onX`) should be called, but without passing a callback. `createAppHook()` will take care of handling the subscription. import connect from '@aragon/connect' import connectVoting from '@aragon/connect-voting' import { createAppHook, useApp } from '@aragon/connect-react' const useVoting = createAppHook(connectVoting) function App() { const [voting, votingStatus] = useApp('voting') // To enable a subscription, use app.onVotes() rather than app.votes(). // Note that the default value can be set using the assignment default. const [votes = [], votesStatus] = useVoting(voting, app => app.onVotes()) return ( // … ) } Of course, it also works with the dependencies array, taking care of unsubscribing and subscribing to the new query: import connect from '@aragon/connect' import connectVoting from '@aragon/connect-voting' import { createAppHook, useApp } from '@aragon/connect-react' const useVoting = createAppHook(connectVoting) function App() { const [page, setPage] = useState(0) const [voting] = useApp('voting') const [votes] = useVoting(voting, app => ( // Note the app.onVotes() rather than app.votes(). app.onVotes({ first: 10, skip: 10 * page }) // The subscription will refresh every time `page` changes. ), [page]) return ( // … ) } Important: this only works with app connectors enabling this syntax. All the core app connectors have been updated for it, but third party app connectors will require to be updated too.
This is a breaking change for app connectors and consumers (with the exception of
@aragon/connect-react
). It is necessary to pass errors with subscriptions, the same way we do it for the async functions by throwing errors.A compatible solution could be to pass the error as a second parameter, but many people are used to Node-style callbacks, so I think it would be too weird to invert the parameters.
Contains the following changes:
SubscriptionCallback
type.