-
Notifications
You must be signed in to change notification settings - Fork 244
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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add context.storefront.mutate
#193
Conversation
packages/hydrogen/storefront.ts
Outdated
const mutationErrors = Object.values(data) | ||
.map((value: any) => value?.customerUserErrors?.[0]?.message) | ||
.filter(Boolean); |
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.
Does this look correct? I'm not very familiar with customerUserErrors
in mutations.
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.
I think this looks right. @frehner what do you think?
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.
I think the client should only handle connection errors
e.g (errors.length)
internally, and should return data
unaltered to let the mutation callee handle the graphql errors on the action (so that we can display them on the UI)
Because cart and customer mutations return errors under different keys cartUserErrors
vs customerUserErrors
, I like to alias both of these as errors
.
This way in an action
we can do:
const {cart, errors} = await storefront.mutation(cartMutation)
const {customer, errors} = await storefront.mutation(customerMutation)
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.
Yeah it's unfortunate that it appears that there are different error properties on different mutations. Ugh.
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.
Ugh I see. The problem is that storefront.query
right now only returns data
... We would need to change the signature from const data =
to const {data, errors} =
. This means we'd need to start adding .then(payload => payload.data)
when using storefront.query
in defer
Nevermind, for some reason I thought the customerUserErrors
array was in errors
instead of data
. I'll update the code to handle the customerUserErrors
on the user app side 馃憤
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.
I don't think you need to change it because both customerUserErrors
and cartUserErrors
are nested within the data
object
There's two types of errors:
- fetch errors: You can deal with these in the client (as you are doing with
errors.length
- graphql errors: We should deal with these on the action/loader
What may have confused you is that I called customerUserErrors
and cartUserErrors
as errors
. But what I meant is this:
const data = await storefront.mutation(cartMutation)
const {cart, cartUserErrors: errors} = data;
Does that make sense?
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.
Ah, I modified my comment earlier without refreshing the page so I didn't see your last comment @juanpprieto , but yeah it makes sense, thanks!
export interface LoaderFunction { | ||
(args: LoaderArgs): Promise<Response> | Response | Promise<AppData> | AppData; | ||
} | ||
|
||
export type ActionArgs = DataFunctionArgs & { | ||
context: HydrogenContext; | ||
}; | ||
|
||
export interface ActionFunction { | ||
(args: ActionArgs): Promise<Response> | Response | Promise<AppData> | AppData; | ||
} |
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.
Aren't these essentially the same as what Remix provides? Why not use their types instead?
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.
I think is because we are modifying DataFunctionArgs['context']?
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 we are modifying context
to be HydrogenContext
, that's the only reason.
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.
馃憤馃徏
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 nit picky items. Great work Fran!
packages/hydrogen/storefront.ts
Outdated
const mutationErrors = Object.values(data) | ||
.map((value: any) => value?.customerUserErrors?.[0]?.message) | ||
.filter(Boolean); |
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.
I think this looks right. @frehner what do you think?
} | ||
|
||
export async function getLayoutData(params: Params) { | ||
export async function getLayoutData( |
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.
Nit picky, but IMO we should get rid of this data index file, and just co-locate the query where it's used, in root.tsx
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.
Yeah I want to do that but perhaps in a different PR more focused on refactoring?
In fact, I know JP is modifying data/index to change things in Cart so I think it's better if we do this later to minimize conflicts with his PR.
@@ -60,7 +61,7 @@ export const action: ActionFunction = async ({request, context, params}) => { | |||
}, | |||
}); | |||
} catch (error: any) { | |||
if (error instanceof StorefrontApiError) { | |||
if (isStorefrontApiError(error)) { |
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.
Interesting, exposing this as a utility function to easily identify storefront api errors 馃
@@ -14,8 +14,7 @@ export async function loader({params, context: {storefront}}: LoaderArgs) { | |||
invariant(params.pageHandle, 'Missing page handle'); | |||
|
|||
const {language} = getLocalizationFromLang(params.lang); | |||
const {page} = await storefront.query<{page: Page}>({ | |||
query: PAGE_QUERY, | |||
const {page} = await storefront.query<{page: Page}>(PAGE_QUERY, { |
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.
I like this change, query
is required, so make it the first arg. Second arg is optional.
packages/hydrogen/storefront.ts
Outdated
const mutationErrors = Object.values(data) | ||
.map((value: any) => value?.customerUserErrors?.[0]?.message) | ||
.filter(Boolean); |
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.
Yeah it's unfortunate that it appears that there are different error properties on different mutations. Ugh.
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 job @frandiox, I agree the separation feels better.
@@ -101,7 +101,7 @@ export const action: ActionFunction = async ({request, context, params}) => { | |||
formDataHas(formData, 'newPassword') && | |||
(customer.password = formData.get('newPassword') as string); | |||
|
|||
await updateCustomer({customerAccessToken, customer}); | |||
await updateCustomer(context, {customerAccessToken, customer}); |
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.
Having to pass context so that I can use the storefront querying / mutations feels verbose, specially in so many functions here.
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.
Yeah we are supposed to remove these functions and just have them inlined in loader
and action
, but that will come in another PR 馃憤
query: string, | ||
payload?: StorefrontCommonOptions & {cache?: CachingStrategy}, | ||
) => { | ||
query = minifyQuery(query); |
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.
Any value in providing an option for users to opt out of minifying for debugging, etc...
As mentioned in the team sync today, this is a suggestion to change the signature of our
storefront.query
function.Before:
After
This seems more straightforward and needs simpler types. However, if we don't like this option I can revert the syntax and keep the other changes 馃憤
Aside from that, this changes the demo-store to start using
context.storefront
and remove the oldgetStorefrontData
utility.It also fixes
LoaderArgs
,ActionArgs
,ActionFunction
andLoaderFunction
types.It's easier to look at this PR commit by commit.