-
Notifications
You must be signed in to change notification settings - Fork 331
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
fix(core): trigger invariant when user doesn't return anything from getItems
#607
Conversation
getItems
getItems
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 3d6e60b:
|
Is the post-resolve check still as useful and understandable? autocomplete/packages/autocomplete-core/src/resolve.ts Lines 173 to 178 in 3e78566
This PR adds a check in pre-resolve, and that one happens in post-resolve. They seem very similar and the wording makes it a bit confusing. I think the only difference is that in post-resolve, we expect a resolved array. |
invariant( | ||
itemsOrDescription !== undefined, | ||
'The `getItems` function should return or resolve to an array of items.' | ||
); |
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.
The existing invariant that we have lives in postResolve
. For consistency, we can move that one to the preResolve
function.
The pre-resolve invariant checks that you did return something from If you want to avoid having two invariants, we can change the internals of the resolving code so that even if you didn't return anything, it flows and resolves to |
@sarahdayan Yes, it would be fewer code paths and errors to grasp user land. |
@@ -171,7 +171,7 @@ export function postResolve<TItem extends BaseItem>( | |||
: results; | |||
|
|||
invariant( | |||
Array.isArray(items), | |||
Array.isArray(items) && (items as Array<typeof items>).every(Boolean), | |||
`The \`getItems\` function must return an array of items but returned type ${JSON.stringify( |
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.
Since we're at it, perhaps we can also display the sourceId
so that the error is more helpful?
`The \`getItems\` function must return an array of items but returned type ${JSON.stringify( | |
`The \`getItems\` function from the source "${source.sourceId}" must return an array of items but returned type ${JSON.stringify( |
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.
Good idea 👌Addressed in d3b7f40.
|
||
return spy.mock.results[0].value; | ||
}).rejects.toThrow( | ||
'[Autocomplete] The `getItems` function must return an array of items but returned type "object":\n\n[\n null\n]' |
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.
[null]
is misleading here since users would expect to see undefined
.
See suggestion below.
@@ -171,7 +171,7 @@ export function postResolve<TItem extends BaseItem>( | |||
: results; | |||
|
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.
To avoid getting the confusing [null]
invariant, perhaps we can have another invariant right above the other, for the special case of getItems
returning undefined
:
invariant( | |
(items as Array<typeof items>).every(Boolean), | |
`The \`getItems\` function must return an array of items but returned ${JSON.stringify(undefined)}. | |
Did you forget to return items? | |
See: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems | |
); |
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.
Yes good call. I've added it after the first invariant, because this one assumes for items
to be an array (of possibly undefined
values). The first invariant throws if items
isn't an array, so as we reach the second one we're covered.
See bdc5b95.
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.
That's great, thanks!
One last note to improve DX a little bit more.
When users don't return anything from
getItems
, they get an unhelpful runtime error:Reproduction link: https://codesandbox.io/s/wizardly-rgb-i69uy (try typing, then see error)
This is because the Requester API determines whether the returned value is a requester description by looking for an
execute
property on them. If there's no return statement ingetItems
, the value isundefined
and the runtime throws.This is fine in production, but not too helpful during development, especially for non-TypeScript users who won't get a type error in their editor. This PR therefore adds an invariant to provide a more helpful development error.
Tests
The test uses a spy on
onInput
because at the level where we're testing, there's no way to track the error. If we wanted to avoid it, it would require testing the error at a lower level, inonInput.ts
(which currently doesn't have a test suite).The current test is closer to what the user would do. Both strategies would leak implementation details, but this one being more expressive regarding what behavior triggers what consequence, I think it's better.