Skip to content

Commit 292dd85

Browse files
ivanCosicoliverlaz
authored andcommitted
[NO-ISSUE] Fixed issue with FetchingProvider remounting children (#14)
* There was an issue with FetchingProvider that it rendered it's children initially, before fetching was started, then rendering null and finally rendering children again after fetching. This happened when blocking was set to true (which is default). This was causing multiple mounts of children, affecting performance. * The issue was caused by misuse of "isFetching" flag in rendering logic since it was initially false, before fetching has even started. * Replacing "isFetching" with "isFetched" is restoring original purpose of "blocking" flag, since rendering should or shouldn't be blocked until data is fetched, not while it is fetching as it was implemented so far.
1 parent 3e88e13 commit 292dd85

File tree

2 files changed

+52
-7
lines changed

2 files changed

+52
-7
lines changed

src/lib/FetchingProvider.test.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,21 @@ import * as RTL from '@testing-library/react';
33
import { FetchingProvider } from './FetchingProvider';
44
import { useMessageSource } from './useMessageSource';
55

6+
const noop = () => {};
7+
68
describe('FetchingProvider', () => {
7-
function Spy() {
9+
type SpyProps = {
10+
onMount?: Function,
11+
};
12+
13+
function Spy({ onMount = noop }: SpyProps) {
814
const { getMessage } = useMessageSource();
15+
16+
// Run onMount only once, when component is mounted initially.
17+
React.useEffect(() => {
18+
onMount();
19+
}, []);
20+
921
return <span>{getMessage('hello.world')}</span>;
1022
}
1123

@@ -98,4 +110,37 @@ describe('FetchingProvider', () => {
98110
expect(faultyFetch).toHaveBeenCalledTimes(1);
99111
expect(onFetchingError).toHaveBeenCalledTimes(1);
100112
});
113+
114+
it('mounts children only once', async () => {
115+
let timesChildrenAreMounted = 0;
116+
const increaseChildrenMountedNumber = () => {
117+
timesChildrenAreMounted += 1;
118+
};
119+
120+
const transform = jest.fn(x => x);
121+
const onFetchingStart = jest.fn();
122+
const onFetchingEnd = jest.fn();
123+
124+
function TestComponent() {
125+
return (
126+
<FetchingProvider
127+
url="http://any.uri/texts?lang=en"
128+
transform={transform}
129+
onFetchingStart={onFetchingStart}
130+
onFetchingEnd={onFetchingEnd}
131+
>
132+
<Spy onMount={increaseChildrenMountedNumber} />
133+
</FetchingProvider>
134+
);
135+
}
136+
137+
const { getByText } = RTL.render(<TestComponent />);
138+
const spyNode = await RTL.waitForElement(() => getByText('Hello world'));
139+
140+
expect(spyNode).toBeDefined();
141+
expect(spyNode.innerHTML).toBe('Hello world');
142+
// @ts-ignore
143+
expect(global.fetch).toHaveBeenCalledTimes(1);
144+
expect(timesChildrenAreMounted).toBe(1);
145+
});
101146
});

src/lib/FetchingProvider.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ export interface FetchingProviderApi {
5050

5151
type State = {
5252
translations: MessageSourceContextShape,
53-
isFetching: boolean,
53+
isFetched: boolean,
5454
};
5555

5656
const initialState: State = {
5757
translations: {},
58-
isFetching: false,
58+
isFetched: false,
5959
};
6060

6161
/**
@@ -73,12 +73,12 @@ export function FetchingProvider(props: FetchingProviderApi) {
7373
onFetchingError = noop,
7474
} = props;
7575

76-
const [{ translations, isFetching }, setState] = React.useState<State>(initialState);
76+
const [{ translations, isFetched }, setState] = React.useState<State>(initialState);
7777

7878
React.useEffect(() => {
7979
let isStillMounted = true;
8080

81-
setState(state => ({ ...state, isFetching: true }));
81+
setState((state: State) => ({ ...state, isFetched: false }));
8282
onFetchingStart();
8383

8484
fetch(url)
@@ -87,7 +87,7 @@ export function FetchingProvider(props: FetchingProviderApi) {
8787
if (isStillMounted) {
8888
setState({
8989
translations: transform(response),
90-
isFetching: false,
90+
isFetched: true,
9191
});
9292
onFetchingEnd();
9393
}
@@ -99,6 +99,6 @@ export function FetchingProvider(props: FetchingProviderApi) {
9999
};
100100
}, [url]); // re-fetch only when url changes
101101

102-
const shouldRenderSubtree = !blocking || (blocking && !isFetching);
102+
const shouldRenderSubtree = !blocking || (blocking && isFetched);
103103
return <Provider value={translations}>{shouldRenderSubtree ? children : null}</Provider>;
104104
}

0 commit comments

Comments
 (0)