Create makeQuery hook

This commit is contained in:
dominik-zeglen 2019-11-14 15:37:32 +01:00
parent b12f3c35fa
commit 8a6cd1ef1f
4 changed files with 470 additions and 375 deletions

View file

@ -1,6 +1,7 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import { pageInfoFragment, TypedQuery } from "../queries"; import makeQuery from "@saleor/hooks/makeQuery";
import { pageInfoFragment } from "../queries";
import { import {
CategoryDetails, CategoryDetails,
CategoryDetailsVariables CategoryDetailsVariables
@ -65,7 +66,7 @@ export const rootCategories = gql`
} }
} }
`; `;
export const TypedRootCategoriesQuery = TypedQuery<RootCategories, {}>( export const useRootCategoriesQuery = makeQuery<RootCategories, {}>(
rootCategories rootCategories
); );
@ -119,7 +120,7 @@ export const categoryDetails = gql`
} }
} }
`; `;
export const TypedCategoryDetailsQuery = TypedQuery< export const useCategoryDetailsQuery = makeQuery<
CategoryDetails, CategoryDetails,
CategoryDetailsVariables CategoryDetailsVariables
>(categoryDetails); >(categoryDetails);

View file

@ -28,7 +28,7 @@ import {
useCategoryDeleteMutation, useCategoryDeleteMutation,
useCategoryUpdateMutation useCategoryUpdateMutation
} from "../mutations"; } from "../mutations";
import { TypedCategoryDetailsQuery } from "../queries"; import { useCategoryDetailsQuery } from "../queries";
import { CategoryBulkDelete } from "../types/CategoryBulkDelete"; import { CategoryBulkDelete } from "../types/CategoryBulkDelete";
import { CategoryDelete } from "../types/CategoryDelete"; import { CategoryDelete } from "../types/CategoryDelete";
import { CategoryUpdate } from "../types/CategoryUpdate"; import { CategoryUpdate } from "../types/CategoryUpdate";
@ -63,6 +63,13 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
); );
const intl = useIntl(); const intl = useIntl();
const paginationState = createPaginationState(PAGINATE_BY, params);
const { data, loading, refetch } = useCategoryDetailsQuery({
displayLoader: true,
require: ["category"],
variables: { ...paginationState, id }
});
const handleCategoryDelete = (data: CategoryDelete) => { const handleCategoryDelete = (data: CategoryDelete) => {
if (data.categoryDelete.errors.length === 0) { if (data.categoryDelete.errors.length === 0) {
notify({ notify({
@ -140,7 +147,6 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
}) })
); );
const paginationState = createPaginationState(PAGINATE_BY, params);
const formTransitionState = getMutationState( const formTransitionState = getMutationState(
updateResult.called, updateResult.called,
updateResult.loading, updateResult.loading,
@ -152,13 +158,6 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
maybe(() => deleteResult.data.categoryDelete.errors) maybe(() => deleteResult.data.categoryDelete.errors)
); );
return (
<TypedCategoryDetailsQuery
displayLoader
variables={{ ...paginationState, id }}
require={["category"]}
>
{({ data, loading, refetch }) => {
const handleBulkProductDelete = (data: productBulkDelete) => { const handleBulkProductDelete = (data: productBulkDelete) => {
if (data.productBulkDelete.errors.length === 0) { if (data.productBulkDelete.errors.length === 0) {
closeModal(); closeModal();
@ -181,23 +180,17 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
return ( return (
<> <>
<WindowTitle title={maybe(() => data.category.name)} /> <WindowTitle title={maybe(() => data.category.name)} />
<TypedProductBulkDeleteMutation <TypedProductBulkDeleteMutation onCompleted={handleBulkProductDelete}>
onCompleted={handleBulkProductDelete}
>
{(productBulkDelete, productBulkDeleteOpts) => { {(productBulkDelete, productBulkDeleteOpts) => {
const categoryBulkDeleteMutationState = getMutationState( const categoryBulkDeleteMutationState = getMutationState(
categoryBulkDeleteOpts.called, categoryBulkDeleteOpts.called,
categoryBulkDeleteOpts.loading, categoryBulkDeleteOpts.loading,
maybe( maybe(() => categoryBulkDeleteOpts.data.categoryBulkDelete.errors)
() => categoryBulkDeleteOpts.data.categoryBulkDelete.errors
)
); );
const productBulkDeleteMutationState = getMutationState( const productBulkDeleteMutationState = getMutationState(
productBulkDeleteOpts.called, productBulkDeleteOpts.called,
productBulkDeleteOpts.loading, productBulkDeleteOpts.loading,
maybe( maybe(() => productBulkDeleteOpts.data.productBulkDelete.errors)
() => productBulkDeleteOpts.data.productBulkDelete.errors
)
); );
return ( return (
@ -207,9 +200,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
currentTab={params.activeTab} currentTab={params.activeTab}
category={maybe(() => data.category)} category={maybe(() => data.category)}
disabled={loading} disabled={loading}
errors={maybe( errors={maybe(() => updateResult.data.categoryUpdate.errors)}
() => updateResult.data.categoryUpdate.errors
)}
onAddCategory={() => navigate(categoryAddUrl(id))} onAddCategory={() => navigate(categoryAddUrl(id))}
onAddProduct={() => navigate(productAddUrl)} onAddProduct={() => navigate(productAddUrl)}
onBack={() => onBack={() =>
@ -252,9 +243,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
id, id,
input: { input: {
backgroundImageAlt: formData.backgroundImageAlt, backgroundImageAlt: formData.backgroundImageAlt,
descriptionJson: JSON.stringify( descriptionJson: JSON.stringify(formData.description),
formData.description
),
name: formData.name, name: formData.name,
seo: { seo: {
description: formData.seoDescription, description: formData.seoDescription,
@ -274,9 +263,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
subcategoryListToolbar={ subcategoryListToolbar={
<IconButton <IconButton
color="primary" color="primary"
onClick={() => onClick={() => openModal("delete-categories", listElements)}
openModal("delete-categories", listElements)
}
> >
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
@ -284,9 +271,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
productListToolbar={ productListToolbar={
<IconButton <IconButton
color="primary" color="primary"
onClick={() => onClick={() => openModal("delete-products", listElements)}
openModal("delete-products", listElements)
}
> >
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
@ -389,8 +374,5 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
</TypedProductBulkDeleteMutation> </TypedProductBulkDeleteMutation>
</> </>
); );
}}
</TypedCategoryDetailsQuery>
);
}; };
export default CategoryDetails; export default CategoryDetails;

View file

@ -19,7 +19,7 @@ import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types"; import { ListViews } from "@saleor/types";
import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage"; import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage";
import { useCategoryBulkDeleteMutation } from "../../mutations"; import { useCategoryBulkDeleteMutation } from "../../mutations";
import { TypedRootCategoriesQuery } from "../../queries"; import { useRootCategoriesQuery } from "../../queries";
import { CategoryBulkDelete } from "../../types/CategoryBulkDelete"; import { CategoryBulkDelete } from "../../types/CategoryBulkDelete";
import { import {
categoryAddUrl, categoryAddUrl,
@ -53,6 +53,20 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
); );
const intl = useIntl(); const intl = useIntl();
const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = React.useMemo(
() => ({
...paginationState,
filter: getFilterVariables(params)
}),
[params]
);
const { data, loading, refetch } = useRootCategoriesQuery({
displayLoader: true,
require: ["categories"],
variables: queryVariables
});
const tabs = getFilterTabs(); const tabs = getFilterTabs();
const currentTab = const currentTab =
@ -113,18 +127,6 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
handleTabChange(tabs.length + 1); handleTabChange(tabs.length + 1);
}; };
const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = React.useMemo(
() => ({
...paginationState,
filter: getFilterVariables(params)
}),
[params]
);
return (
<TypedRootCategoriesQuery displayLoader variables={queryVariables}>
{({ data, loading, refetch }) => {
const { loadNextPage, loadPreviousPage, pageInfo } = paginate( const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
maybe(() => data.categories.pageInfo), maybe(() => data.categories.pageInfo),
paginationState, paginationState,
@ -226,9 +228,7 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
defaultMessage="Are you sure you want to delete {counter,plural,one{this category} other{{displayQuantity} categories}}?" defaultMessage="Are you sure you want to delete {counter,plural,one{this category} other{{displayQuantity} categories}}?"
values={{ values={{
counter: maybe(() => params.ids.length), counter: maybe(() => params.ids.length),
displayQuantity: ( displayQuantity: <strong>{maybe(() => params.ids.length)}</strong>
<strong>{maybe(() => params.ids.length)}</strong>
)
}} }}
/> />
</DialogContentText> </DialogContentText>
@ -251,8 +251,5 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
/> />
</> </>
); );
}}
</TypedRootCategoriesQuery>
);
}; };
export default CategoryList; export default CategoryList;

115
src/hooks/makeQuery.ts Normal file
View file

@ -0,0 +1,115 @@
import { ApolloQueryResult } from "apollo-client";
import { DocumentNode } from "graphql";
import { useEffect } from "react";
import { QueryResult, useQuery as useBaseQuery } from "react-apollo";
import { useIntl } from "react-intl";
import { commonMessages } from "@saleor/intl";
import { maybe, RequireAtLeastOne } from "@saleor/misc";
import useAppState from "./useAppState";
import useNotifier from "./useNotifier";
export interface LoadMore<TData, TVariables> {
loadMore: (
mergeFunc: (prev: TData, next: TData) => TData,
extraVariables: Partial<TVariables>
) => Promise<ApolloQueryResult<TData>>;
}
type UseQuery<TData, TVariables> = QueryResult<TData, TVariables> &
LoadMore<TData, TVariables>;
type UseQueryOpts<TData, TVariables> = Partial<{
displayLoader: boolean;
require: Array<keyof TData>;
skip: boolean;
variables: TVariables;
}>;
type UseQueryHook<TData, TVariables> = (
opts: UseQueryOpts<TData, TVariables>
) => UseQuery<TData, TVariables>;
function makeQuery<TData, TVariables>(
query: DocumentNode
): UseQueryHook<TData, TVariables> {
function useQuery<TData, TVariables>({
displayLoader,
require,
skip,
variables
}: UseQueryOpts<TData, TVariables>): UseQuery<TData, TVariables> {
const notify = useNotifier();
const intl = useIntl();
const [, dispatchAppState] = useAppState();
const queryData = useBaseQuery(query, {
context: {
useBatching: true
},
errorPolicy: "all",
fetchPolicy: "cache-and-network",
skip,
variables
});
useEffect(() => {
if (displayLoader) {
dispatchAppState({
payload: {
value: queryData.loading
},
type: "displayLoader"
});
}
}, [queryData.loading]);
if (queryData.error) {
if (
!queryData.error.graphQLErrors.every(
err =>
maybe(() => err.extensions.exception.code) === "PermissionDenied"
)
) {
notify({
text: intl.formatMessage(commonMessages.somethingWentWrong)
});
}
}
const loadMore = (
mergeFunc: (previousResults: TData, fetchMoreResult: TData) => TData,
extraVariables: RequireAtLeastOne<TVariables>
) =>
queryData.fetchMore({
query,
updateQuery: (previousResults, { fetchMoreResult }) => {
if (!fetchMoreResult) {
return previousResults;
}
return mergeFunc(previousResults, fetchMoreResult);
},
variables: { ...variables, ...extraVariables }
});
if (
!queryData.loading &&
require &&
queryData.data &&
!require.reduce((acc, key) => acc && queryData.data[key] !== null, true)
) {
dispatchAppState({
payload: {
error: "not-found"
},
type: "displayError"
});
}
return {
...queryData,
loadMore
};
}
return useQuery;
}
export default makeQuery;