Skip to content

Commit

Permalink
Add example with side effect in render got triggered directly (#38830)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #38830

Changelog:
[Internal] - Add example with side effect in render got triggered directly

Reviewed By: rshest

Differential Revision: D48055836

fbshipit-source-id: 44544ebf19525bf43e1a78104bc870aecf36c7c9
  • Loading branch information
Xin Chen authored and facebook-github-bot committed Aug 16, 2023
1 parent 17e240b commit 5a1c728
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/

'use strict';

import type {ItemDataType} from '../components/itemData';

import * as React from 'react';
import {useState, useEffect, useCallback, StrictMode} from 'react';
import {Text} from 'react-native';
import {generateRandomItems} from '../components/itemData';
import ItemList from '../components/ItemList';

const TIMEOUT = 500;
const FETCH_COUNT = 250;
const ItemListMemo = React.memo(ItemList);

function ItemFetcherBadExample(props: {
onFetched: (items: ItemDataType[]) => void,
count: number,
}): React.Node {
const {onFetched, count} = props;
const fetchMoreItems = async (
itemsCount: number,
): Promise<ItemDataType[]> => {
return new Promise(resolve => {
setTimeout(() => {
resolve(generateRandomItems(itemsCount));
}, TIMEOUT);
});
};

fetchMoreItems(count).then((items: ItemDataType[]) => {
onFetched(items);
}, console.error);
}

function ItemFetcherGoodExample(props: {
onFetched: (items: ItemDataType[]) => void,
count: number,
}): React.Node {
const {onFetched, count} = props;
useEffect(() => {
const fetchMoreItems = async (
itemsCount: number,
): Promise<ItemDataType[]> => {
return new Promise(resolve => {
setTimeout(() => {
resolve(generateRandomItems(itemsCount));
}, TIMEOUT);
});
};

fetchMoreItems(count).then((items: ItemDataType[]) => {
onFetched(items);
}, console.error);
}, [onFetched, count]);
}

const ItemFetcherBadExampleMemo = React.memo(ItemFetcherBadExample);
const ItemFetcherGoodExampleMemo = React.memo(ItemFetcherGoodExample);
function EffectInRenderBadExample(): React.Node {
const [visibleItems, setVisibleItems] = useState<ItemDataType[]>([]);
const [fetchedItems, setFetchedItems] = useState<ItemDataType[]>([]);
const onMoreItemFetched = useCallback(
(items: ItemDataType[]) => {
setFetchedItems(items);
},
[setFetchedItems],
);

if (fetchedItems.length > 0) {
setVisibleItems(visibleItems.concat(fetchedItems));
setFetchedItems([]);
}

return (
<StrictMode>
<Text>{`Items count in list: ${visibleItems.length}`}</Text>
<ItemFetcherBadExampleMemo
onFetched={onMoreItemFetched}
count={FETCH_COUNT}
/>
<ItemListMemo data={visibleItems} />
</StrictMode>
);
}

function EffectInRenderGoodExample(): React.Node {
const [visibleItems, setVisibleItems] = useState<ItemDataType[]>([]);
const [fetchedItems, setFetchedItems] = useState<ItemDataType[]>([]);
const onMoreItemFetched = useCallback(
(items: ItemDataType[]) => {
setFetchedItems(items);
},
[setFetchedItems],
);

if (fetchedItems.length > 0) {
setVisibleItems(visibleItems.concat(fetchedItems));
setFetchedItems([]);
}

return (
<StrictMode>
<Text>{`Items count in list: ${visibleItems.length}`}</Text>
<ItemFetcherGoodExampleMemo
onFetched={onMoreItemFetched}
count={FETCH_COUNT}
/>
<ItemListMemo data={visibleItems} />
</StrictMode>
);
}

export default {
title:
'Directly trigger side effect in render may run multiple times and cause error state or re-renders',
description:
'Trigger a side effect in render method without using effect hook in <StrictMode>. This will force render component two times, and the side effect in bad example caused append items to the list two times unexpectedly.',
Bad: EffectInRenderBadExample,
Good: EffectInRenderGoodExample,
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export {default as ReRenderWithObjectPropExample} from './ReRenderWithObjectProp
export {default as SetStateInWrongEffectExample} from './SetStateInWrongEffectExample';
export {default as RenderOffscreenContentExample} from './RenderOffscreenContentExample';
export {default as NotMemoizeExpensiveTaskExample} from './NotMemoizeExpensiveTaskExample';
export {default as EffectInRenderExample} from './EffectInRenderExample';

0 comments on commit 5a1c728

Please sign in to comment.