Skip to content

Commit 21c00f5

Browse files
authored
fix(useFetchye): forceInitialFetch (#102)
* fix(useFetchye): forceInitialFetch * fix(useFetchye): forceInitialFetch * fix(useFetchye): forceInitialFetch * fix(useFetchye): forceInitialFetch * fix(useFetchye): forceInitialFetch * fix(useFetchye): forceInitialFetch * chore(useFetchye): apply suggestion * chore(useFetchye): apply suggestion * chore(useFetchye): apply suggestion
1 parent eb7934a commit 21c00f5

File tree

6 files changed

+118
-41
lines changed

6 files changed

+118
-41
lines changed

packages/fetchye/__tests__/defaultMapOptionsToKey.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
import { defaultMapOptionsToKey } from '../src/defaultMapOptionsToKey';
1818

1919
describe('defaultMapOptionsToKey', () => {
20-
it('should return an object without passed signal, defer, or mapOptionsToKey', () => {
20+
it('should return an object without passed signal, defer, mapOptionsToKey, or forceInitialFetch', () => {
2121
expect(defaultMapOptionsToKey({
22-
signal: {}, defer: true, mapOptionsToKey: () => { }, method: 'POST',
22+
signal: {}, defer: true, mapOptionsToKey: () => { }, method: 'POST', forceInitialFetch: true,
2323
}))
2424
.toMatchInlineSnapshot(`
2525
Object {

packages/fetchye/__tests__/queryHelpers.spec.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ describe('isLoading', () => {
2929
loading: false, data: undefined, numOfRenders: 1, options: { },
3030
})).toEqual(true);
3131
});
32+
it('should return true if first render and forceInitialFetch option is true', () => {
33+
expect(isLoading({
34+
loading: false, data: undefined, numOfRenders: 1, options: { forceInitialFetch: true },
35+
})).toEqual(true);
36+
});
3237
it('should return false if first render is true and defer is true', () => {
3338
expect(isLoading({
3439
loading: false, data: undefined, numOfRenders: 1, options: { defer: true },

packages/fetchye/__tests__/useFetchye.spec.jsx

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* permissions and limitations under the License.
1515
*/
1616

17-
import React, { useRef } from 'react';
17+
import React, { useEffect, useRef, useState } from 'react';
1818
import { render, waitFor } from '@testing-library/react';
1919
import { Provider } from 'react-redux';
2020
import { createStore } from 'redux';
@@ -348,33 +348,12 @@ describe('useFetchye', () => {
348348
"initialData": true,
349349
},
350350
},
351-
"error": null,
351+
"error": undefined,
352352
"isLoading": false,
353353
"run": [Function],
354354
}
355355
`);
356356
});
357-
it('should ignore cache', async () => {
358-
let fetchyeRes;
359-
global.fetch = jest.fn(async () => ({ ...defaultPayload }));
360-
render(
361-
<AFetchyeProvider cache={cache}>
362-
{React.createElement(() => {
363-
const { isLoading } = useFetchye('http://example.com/one');
364-
if (isLoading === true) {
365-
return null;
366-
}
367-
return React.createElement(() => {
368-
fetchyeRes = useFetchye('http://example.com/one', { forceInitialFetch: true });
369-
return null;
370-
});
371-
})}
372-
</AFetchyeProvider>
373-
);
374-
await waitFor(() => fetchyeRes.isLoading === false);
375-
376-
expect(global.fetch.mock.calls).toHaveLength(2);
377-
});
378357
});
379358
});
380359

@@ -401,6 +380,65 @@ describe('useFetchye', () => {
401380
);
402381
expect(fakeFetchClient).toHaveBeenCalledTimes(1);
403382
});
383+
it('should ignore cache', async () => {
384+
global.fetch = jest.fn()
385+
.mockImplementationOnce(async () => ({
386+
...defaultPayload,
387+
text: async () => JSON.stringify({
388+
fakeData: true,
389+
fetchNo: 'first',
390+
}),
391+
}))
392+
.mockImplementationOnce(async () => ({
393+
...defaultPayload,
394+
text: async () => JSON.stringify({
395+
fakeData: true,
396+
fetchNo: 'second',
397+
}),
398+
}));
399+
let firstRes;
400+
let secondRes;
401+
// eslint-disable-next-line react/prop-types -- no need for test here
402+
const TestComp = ({ forceFetch, firstResponse, setRes }) => {
403+
const res = useFetchye('http://example.com/one', forceFetch ? { forceInitialFetch: forceFetch } : undefined);
404+
if (forceFetch) {
405+
secondRes = res;
406+
} else {
407+
firstRes = res;
408+
}
409+
useEffect(() => {
410+
if (!firstResponse && res.data?.body.fetchNo === 'first' && !forceFetch) {
411+
setRes(res);
412+
}
413+
}, [firstResponse, res, setRes, forceFetch]);
414+
if (res.isLoading || !res.data) {
415+
return null;
416+
}
417+
return <p>{res.data.body.fetchNo}</p>;
418+
};
419+
const Comp = () => {
420+
const [firstResponse, setRes] = useState();
421+
return (
422+
<AFetchyeProvider cache={cache}>
423+
<TestComp forceFetch={false} firstResponse={firstResponse} setRes={setRes} />
424+
{firstResponse?.data && <TestComp forceFetch={true} />}
425+
</AFetchyeProvider>
426+
);
427+
};
428+
render(<Comp />);
429+
await waitFor(() => {
430+
expect(firstRes.data?.body.fetchNo).toBe('first');
431+
});
432+
await waitFor(() => {
433+
expect(secondRes.data?.body).toStrictEqual({
434+
fakeData: true,
435+
fetchNo: 'second',
436+
});
437+
});
438+
expect(secondRes.isLoading).toBeFalsy();
439+
expect(secondRes.error).toBeUndefined();
440+
expect(global.fetch.mock.calls).toHaveLength(2);
441+
});
404442
});
405443
});
406444
});

packages/fetchye/src/defaultMapOptionsToKey.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const defaultMapOptionsToKey = (options) => {
2020
defer,
2121
mapOptionsToKey,
2222
initialData,
23+
forceInitialFetch,
2324
...restOfOptions
2425
} = options;
2526
return restOfOptions;

packages/fetchye/src/queryHelpers.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ export const isLoading = ({
2929
// isLoading should be true
3030
return true;
3131
}
32+
// when we force fetch isLoading is always going to be true on first render
33+
if (options.forceInitialFetch) {
34+
// isLoading should be true
35+
return true;
36+
}
3237
}
3338
// If not on first render and loading from cache is true
3439
if (loading) {

packages/fetchye/src/useFetchye.js

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,30 @@
1515
*/
1616

1717
import { useEffect, useRef } from 'react';
18+
import { setAction } from 'fetchye-core';
1819
import { runAsync } from './runAsync';
1920
import { computeKey } from './computeKey';
2021
import {
2122
isLoading,
2223
} from './queryHelpers';
2324
import { useFetchyeContext } from './useFetchyeContext';
2425

25-
const passInitialData = (value, initialValue, numOfRenders) => (numOfRenders === 1
26-
? value || initialValue
27-
: value);
26+
const passInitialData = ({
27+
value,
28+
initialValue,
29+
numOfRenders,
30+
forceInitialFetch,
31+
}) => {
32+
if (numOfRenders === 1) {
33+
if (initialValue) {
34+
return initialValue;
35+
}
36+
if (forceInitialFetch === true) {
37+
return undefined;
38+
}
39+
}
40+
return value;
41+
};
2842

2943
const useFetchye = (
3044
key,
@@ -37,6 +51,7 @@ const useFetchye = (
3751
const selectedFetcher = typeof fetcher === 'function' ? fetcher : defaultFetcher;
3852
const computedKey = computeKey(key, options);
3953
const selectorState = useFetchyeSelector(computedKey.hash);
54+
const forceInitialFetch = useRef(options?.forceInitialFetch || false);
4055
// create a render version manager using refs
4156
const numOfRenders = useRef(0);
4257
numOfRenders.current += 1;
@@ -50,18 +65,25 @@ const useFetchye = (
5065
return;
5166
}
5267
const { loading, data, error } = selectorState.current;
53-
// If first render and options.forceInitialFetch is true we want to fetch from server
54-
// on first render.
55-
if (
56-
(!loading && !data && !error)
57-
|| (numOfRenders.current === 1 && options.forceInitialFetch === true)
58-
) {
68+
69+
if (data && forceInitialFetch.current) {
70+
// This is so it clears the cache before the forceFetch so we don't have isLoading true
71+
// and data also defined from the cached value.
72+
dispatch(setAction({ hash: computedKey.hash, value: undefined }));
5973
runAsync({
6074
dispatch, computedKey, fetcher: selectedFetcher, fetchClient, options,
6175
});
76+
forceInitialFetch.current = false;
77+
return;
6278
}
63-
});
6479

80+
if (!loading && !data && !error) {
81+
runAsync({
82+
dispatch, computedKey, fetcher: selectedFetcher, fetchClient, options,
83+
});
84+
forceInitialFetch.current = false;
85+
}
86+
});
6587
return {
6688
isLoading: isLoading({
6789
loading: selectorState.current.loading,
@@ -70,14 +92,20 @@ const useFetchye = (
7092
options,
7193
}),
7294
error: passInitialData(
73-
selectorState.current.error,
74-
options.initialData?.error,
75-
numOfRenders.current
95+
{
96+
value: selectorState.current.error,
97+
initialValue: options.initialData?.error,
98+
numOfRenders: numOfRenders.current,
99+
forceInitialFetch: options.forceInitialFetch,
100+
}
76101
),
77102
data: passInitialData(
78-
selectorState.current.data,
79-
options.initialData?.data,
80-
numOfRenders.current
103+
{
104+
value: selectorState.current.data,
105+
initialValue: options.initialData?.data,
106+
numOfRenders: numOfRenders.current,
107+
forceInitialFetch: options.forceInitialFetch,
108+
}
81109
),
82110
run() {
83111
return runAsync({

0 commit comments

Comments
 (0)