Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/fetchye/__tests__/defaultMapOptionsToKey.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
import { defaultMapOptionsToKey } from '../src/defaultMapOptionsToKey';

describe('defaultMapOptionsToKey', () => {
it('should return an object without passed signal, defer, or mapOptionsToKey', () => {
it('should return an object without passed signal, defer, mapOptionsToKey, or forceInitialFetch', () => {
expect(defaultMapOptionsToKey({
signal: {}, defer: true, mapOptionsToKey: () => { }, method: 'POST',
signal: {}, defer: true, mapOptionsToKey: () => { }, method: 'POST', forceInitialFetch: true,
}))
.toMatchInlineSnapshot(`
Object {
Expand Down
5 changes: 5 additions & 0 deletions packages/fetchye/__tests__/queryHelpers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ describe('isLoading', () => {
loading: false, data: undefined, numOfRenders: 1, options: { },
})).toEqual(true);
});
it('should return true if first render and forceInitialFetch option is true', () => {
expect(isLoading({
loading: false, data: undefined, numOfRenders: 1, options: { forceInitialFetch: true },
})).toEqual(true);
});
it('should return false if first render is true and defer is true', () => {
expect(isLoading({
loading: false, data: undefined, numOfRenders: 1, options: { defer: true },
Expand Down
84 changes: 61 additions & 23 deletions packages/fetchye/__tests__/useFetchye.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* permissions and limitations under the License.
*/

import React, { useRef } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { render, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
Expand Down Expand Up @@ -348,33 +348,12 @@ describe('useFetchye', () => {
"initialData": true,
},
},
"error": null,
"error": undefined,
"isLoading": false,
"run": [Function],
}
`);
});
it('should ignore cache', async () => {
let fetchyeRes;
global.fetch = jest.fn(async () => ({ ...defaultPayload }));
render(
<AFetchyeProvider cache={cache}>
{React.createElement(() => {
const { isLoading } = useFetchye('http://example.com/one');
if (isLoading === true) {
return null;
}
return React.createElement(() => {
fetchyeRes = useFetchye('http://example.com/one', { forceInitialFetch: true });
return null;
});
})}
</AFetchyeProvider>
);
await waitFor(() => fetchyeRes.isLoading === false);

expect(global.fetch.mock.calls).toHaveLength(2);
});
});
});

Expand All @@ -401,6 +380,65 @@ describe('useFetchye', () => {
);
expect(fakeFetchClient).toHaveBeenCalledTimes(1);
});
it('should ignore cache', async () => {
global.fetch = jest.fn()
.mockImplementationOnce(async () => ({
...defaultPayload,
text: async () => JSON.stringify({
fakeData: true,
fetchNo: 'first',
}),
}))
.mockImplementationOnce(async () => ({
...defaultPayload,
text: async () => JSON.stringify({
fakeData: true,
fetchNo: 'second',
}),
}));
let firstRes;
let secondRes;
// eslint-disable-next-line react/prop-types -- no need for test here
const TestComp = ({ forceFetch, firstResponse, setRes }) => {
const res = useFetchye('http://example.com/one', forceFetch ? { forceInitialFetch: forceFetch } : undefined);
if (forceFetch) {
secondRes = res;
} else {
firstRes = res;
}
useEffect(() => {
if (!firstResponse && res.data?.body.fetchNo === 'first' && !forceFetch) {
setRes(res);
}
}, [firstResponse, res, setRes, forceFetch]);
if (res.isLoading || !res.data) {
return null;
}
return <p>{res.data.body.fetchNo}</p>;
};
const Comp = () => {
const [firstResponse, setRes] = useState();
return (
<AFetchyeProvider cache={cache}>
<TestComp forceFetch={false} firstResponse={firstResponse} setRes={setRes} />
{firstResponse?.data && <TestComp forceFetch={true} />}
</AFetchyeProvider>
);
};
render(<Comp />);
await waitFor(() => {
expect(firstRes.data?.body.fetchNo).toBe('first');
});
await waitFor(() => {
expect(secondRes.data?.body).toStrictEqual({
fakeData: true,
fetchNo: 'second',
});
});
expect(secondRes.isLoading).toBeFalsy();
expect(secondRes.error).toBeUndefined();
expect(global.fetch.mock.calls).toHaveLength(2);
});
});
});
});
Expand Down
1 change: 1 addition & 0 deletions packages/fetchye/src/defaultMapOptionsToKey.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const defaultMapOptionsToKey = (options) => {
defer,
mapOptionsToKey,
initialData,
forceInitialFetch,
...restOfOptions
} = options;
return restOfOptions;
Expand Down
5 changes: 5 additions & 0 deletions packages/fetchye/src/queryHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export const isLoading = ({
// isLoading should be true
return true;
}
// when we force fetch isLoading is always going to be true on first render
if (options.forceInitialFetch) {
// isLoading should be true
return true;
}
}
// If not on first render and loading from cache is true
if (loading) {
Expand Down
60 changes: 44 additions & 16 deletions packages/fetchye/src/useFetchye.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,30 @@
*/

import { useEffect, useRef } from 'react';
import { setAction } from 'fetchye-core';
import { runAsync } from './runAsync';
import { computeKey } from './computeKey';
import {
isLoading,
} from './queryHelpers';
import { useFetchyeContext } from './useFetchyeContext';

const passInitialData = (value, initialValue, numOfRenders) => (numOfRenders === 1
? value || initialValue
: value);
const passInitialData = ({
value,
initialValue,
numOfRenders,
forceInitialFetch,
}) => {
if (numOfRenders === 1) {
if (initialValue) {
return initialValue;
}
if (forceInitialFetch === true) {
return undefined;
}
}
return value;
};

const useFetchye = (
key,
Expand All @@ -37,6 +51,7 @@ const useFetchye = (
const selectedFetcher = typeof fetcher === 'function' ? fetcher : defaultFetcher;
const computedKey = computeKey(key, options);
const selectorState = useFetchyeSelector(computedKey.hash);
const forceInitialFetch = useRef(options?.forceInitialFetch || false);
// create a render version manager using refs
const numOfRenders = useRef(0);
numOfRenders.current += 1;
Expand All @@ -50,18 +65,25 @@ const useFetchye = (
return;
}
const { loading, data, error } = selectorState.current;
// If first render and options.forceInitialFetch is true we want to fetch from server
// on first render.
if (
(!loading && !data && !error)
|| (numOfRenders.current === 1 && options.forceInitialFetch === true)
) {

if (data && forceInitialFetch.current) {
// This is so it clears the cache before the forceFetch so we don't have isLoading true
// and data also defined from the cached value.
dispatch(setAction({ hash: computedKey.hash, value: undefined }));
runAsync({
dispatch, computedKey, fetcher: selectedFetcher, fetchClient, options,
});
forceInitialFetch.current = false;
return;
}
});

if (!loading && !data && !error) {
runAsync({
dispatch, computedKey, fetcher: selectedFetcher, fetchClient, options,
});
forceInitialFetch.current = false;
}
});
return {
isLoading: isLoading({
loading: selectorState.current.loading,
Expand All @@ -70,14 +92,20 @@ const useFetchye = (
options,
}),
error: passInitialData(
selectorState.current.error,
options.initialData?.error,
numOfRenders.current
{
value: selectorState.current.error,
initialValue: options.initialData?.error,
numOfRenders: numOfRenders.current,
forceInitialFetch: options.forceInitialFetch,
}
),
data: passInitialData(
selectorState.current.data,
options.initialData?.data,
numOfRenders.current
{
value: selectorState.current.data,
initialValue: options.initialData?.data,
numOfRenders: numOfRenders.current,
forceInitialFetch: options.forceInitialFetch,
}
),
run() {
return runAsync({
Expand Down
Loading