Fix awkward blinky loading state for outfit

When loading an outfit in the wardrobe page, there was an awkward state where the outfit preview loading spinner would vanish and then reappear.

This was because `useOutfitState` briefly reported `loading: false`, then fixed itself after almost immediately—but our OutfitPreview component has a delay before re-showing the spinner.

In this change, we smooth out the loading state, by enabling the second GQL request to start immediately once the first request is done, instead of waiting on a callback to finish.
This commit is contained in:
Emi Matchu 2021-01-08 03:19:57 -08:00
parent 62579dc671
commit c7929b29a5

View file

@ -18,14 +18,6 @@ function useOutfitState() {
initialState initialState
); );
const { id, speciesId, colorId, pose, appearanceId } = state;
// It's more convenient to manage these as a Set in state, but most callers
// will find it more convenient to access them as arrays! e.g. for `.map()`
const wornItemIds = Array.from(state.wornItemIds);
const closetedItemIds = Array.from(state.closetedItemIds);
const allItemIds = [...state.wornItemIds, ...state.closetedItemIds];
// If there's an outfit ID (i.e. we're on /outfits/:id), load basic data // If there's an outfit ID (i.e. we're on /outfits/:id), load basic data
// about the outfit. We'll use it to initialize the local state. // about the outfit. We'll use it to initialize the local state.
const { const {
@ -61,13 +53,16 @@ function useOutfitState() {
} }
`, `,
{ {
variables: { id }, variables: { id: state.id },
skip: id == null, skip: state.id == null,
returnPartialData: true, returnPartialData: true,
onCompleted: (outfitData) => { onCompleted: (outfitData) => {
console.log(outfitData);
// This is only called once the _entire_ query loads, regardless of // This is only called once the _entire_ query loads, regardless of
// `returnPartialData`. We just use that for some early UI! // `returnPartialData`. We just use that for some early UI!
//
// Even though we do a HACK to make these values visible early, we
// still want to write them to state, so that reducers can see them and
// edit them!
const outfit = outfitData.outfit; const outfit = outfitData.outfit;
dispatchToOutfit({ dispatchToOutfit({
type: "reset", type: "reset",
@ -82,9 +77,29 @@ function useOutfitState() {
} }
); );
// Let the outfit name appear early, from partial data, even if the full // HACK: We fall back to outfit data here, to help the loading states go
// outfit state isn't initialized yet. // smoother. (Otherwise, there's a flicker where `outfitLoading` is false,
const name = state.name || outfitData?.outfit?.name; // but the `reset` action hasn't fired yet.) This also enables partial outfit
// data to show early, like the name, if we're navigating from Your Outfits.
//
// We also call `Array.from` on our item IDs. It's more convenient to manage
// them as a Set in state, but most callers will find it more convenient to
// access them as arrays! e.g. for `.map()`.
const outfit = outfitData?.outfit;
const id = state.id;
const name = state.name || outfit?.name;
const speciesId = state.speciesId || outfit?.petAppearance?.species?.id;
const colorId = state.colorId || outfit?.petAppearance?.color?.id;
const pose = state.pose || outfit?.petAppearance?.pose;
const appearanceId = state?.appearanceId;
const wornItemIds = Array.from(
state.wornItemIds || outfit?.wornItems?.map((i) => i.id)
);
const closetedItemIds = Array.from(
state.closetedItemIds || outfit?.closetedItems?.map((i) => i.id)
);
const allItemIds = [...state.wornItemIds, ...state.closetedItemIds];
const { const {
loading: itemsLoading, loading: itemsLoading,