typescript - Apollo-klient MockedProvider: Hopp over lastetilstand
Jeg har satt opp en React-app med Apollo for å ha en wrapper-komponent som håndterer lasting av data fra nettverket, slik at hver komponent under kan hente data direkte fra Apollo-cachen uten å måtte bekymre seg for håndtering av lastetilstander. Dermed ser komponentene mine slik ut:
export const COUNT_OFF_ADAPTER_QUERY = gql`
query CountOffAdapterQuery($vampId: ID!) {
vamp(id: $vampId) @client {
id
countingOff
countingOffStartTime
}
}
`;
export const CountOffAdapter: React.FC<{}> = () => {
const vampId = useCurrentVampId();
const {
data: {
vamp: { countingOff, countingOffStartTime }
}
} = useQuery<CountOffAdapterQuery>(COUNT_OFF_ADAPTER_QUERY, {
variables: { vampId }
});
const prev = usePrevious({ countingOff, countingOffStartTime });
const countOff = useCountOff();
useEffect(() => {
// Do react stuff
}, [countOff, countingOff, prev]);
return null;
};
I praksis (i nettleseren) fungerer dette veldig bra siden i appen vil Apollo returnere dataene umiddelbart fra cache, så loading-resultatet fra useQuery-kallet er aldri true og jeg kan destrukturere dataene, bruke dem i påfølgende hooks, osv.
IMIDLERTID, når det kommer tid for testing, ser det ut til at MockedProvider nødvendigvis returnerer loading som true, uten å gi noen kontroll til testskriveren om å hoppe over den tilstanden (noe jeg åpenbart gjerne vil gjøre). Merk at når useQuery returnerer loading som true, vil rendering av komponenten min føre til en feil, siden den destrukturerer data, som er undefined. På grunn av dette kan jeg aldri komme forbi den lastende tilstanden uten at testen feiler.
Her er testkoden min:
it("works", async () => {
(useCurrentVampId as jest.Mock).mockImplementation(
() => "6070dedd58d7bf715eb2a6c5"
);
const component = mount(
<MockedProvider
mocks={[
{
request: {
query: COUNT_OFF_ADAPTER_QUERY,
variables: {
vampId: "6070dedd58d7bf715eb2a6c5"
}
},
result: {
data: {
vamp: {
id: "6070dedd58d7bf715eb2a6c5",
countingOff: true,
countingOffStartTime: 1234
}
}
}
}
]}
addTypename={false}
>
<CountOffAdapter></CountOffAdapter>
</MockedProvider>
);
await wait(0);
component.update();
});
Jeg er nysgjerrig på om det finnes en måte å omgå dette på. Det hadde vært fint om du kunne spesifisere loading: false i spørringsmocken, men det kan du ikke.
Svar #1
Jeg bruker Next.js og jeg gjør mest server-side rendering, så jeg støtte på den samme situasjonen som deg og bestemte meg for å undersøke litt nærmere. Det ser ut til at apollo-client tilbyr delay-prop i selve mocken, for eksempel:
const mock: MockedResponse = {
request: {
query: SomeDocument,
variables: { slug: 'test' },
},
result: { data: fakeData },
delay: 0, // <---------THIS ----------|(number or undefined)
};
Men dette hjalp heller ikke, så jeg gikk dypere inn i koden og alt ble klart etter at jeg så følgende:
return new Observable(observer => {
const timer = setTimeout(() => {
if (configError) {
try {
// The onError function can return false to indicate that
// configError need not be passed to observer.error. For
// example, the default implementation of onError calls
// observer.error(configError) and then returns false to
// prevent this extra (harmless) observer.error call.
if (this.onError(configError, observer) !== false) {
throw configError;
}
} catch (error) {
observer.error(error);
}
} else if (response) {
if (response.error) {
observer.error(response.error);
} else {
if (response.result) {
observer.next(
typeof response.result === 'function'
? (response.result as ResultFunction<FetchResult>)()
: response.result
);
}
observer.complete();
}
}
}, response && response.delay || 0);
Som du kan se her, brukes forsinkelsen bare som et millisekund-argument til setTimeout, som er en asynkron kode -> betyr at den vil bli lagt til etter den synkrone koden i JavaScript-køen (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop). Så det spiller egentlig ingen rolle hvilken verdi du oppgir for forsinkelsesfeltet.
Endelig konklusjon på spørsmålet ditt: Det finnes ingen måte å hoppe over lastetilstanden på nåværende tidspunkt.
