React - JSX 元素的 useCallback 与 useMemo
React - useCallback vs useMemo for JSX elements
我已经实现了这个组件:
function CardList({
data = [],
isLoading = false,
ListHeaderComponent,
ListEmptyComponent,
...props
}) {
const keyExtractor = useCallback(({ id }) => id, []);
const renderItem = useCallback(
({ item, index }) => (
<Card
data={item}
onLayout={(event) => {
itemHeights.current[index] = event.nativeEvent.layout.height;
}}
/>
),
[]
);
const renderFooter = useCallback(() => {
if (!isLoading) return null;
return (
<View style={globalStyles.listFooter}>
<Loading />
</View>
);
}, [isLoading]);
return (
<FlatList
{...props}
data={data}
keyExtractor={keyExtractor}
renderItem={renderItem}
ListHeaderComponent={ListHeaderComponent}
ListFooterComponent={renderFooter()}
ListEmptyComponent={ListEmptyComponent}
/>
);
}
由于我的 CardList 组件很重,我尝试按照这些 tips.
对其进行优化
但是,我认为我应该使用 useMemo
而不是对 renderFooter
使用 useCallback
,以便记住生成的 JSX 而不是方法:
const ListFooterComponent = useMemo(() => {
if (!isLoading) return null;
return (
<View style={globalStyles.listFooter}>
<Loading />
</View>
);
}, [isLoading]);
我说得对吗?
“应该”是见仁见智的事情,但你当然可以。 React 元素是完全可重用的。但是,它不太可能对您的组件产生任何真正的影响,因为创建元素很快并且 renderFooter
只是立即用于已经 运行 的组件(不像 keyExtractor
和 renderItem
,您要将其传递给 FlatList
,因此您希望尽可能使它们稳定,以便 FlatList
可以优化其 re-rendering)。但你当然可以做到。
useMemo
在这里非常明智,因为它会记住结果(JSX)。正如您所说,useCallback
记住了 函数 而不是结果。
如果您想避免昂贵的计算,请使用useMemo
。 useCallback
用于记忆 callback/function.
Official 文档确实有一个将 useMemo
与 JSX 一起使用以避免重新渲染的示例:
function Parent({ a, b }) {
// Only re-rendered if `a` changes:
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// Only re-rendered if `b` changes:
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
}
个人观察:
但更详细地说,似乎并不是 useMemo
这里神奇地阻止了重新渲染,而是你在组件层次结构中的同一位置渲染 相同的引用, 使 react 跳过重新渲染。
这里:
let Child = (props) => {
console.log('rendered', props.name);
return <div>Child</div>;
};
export default function Parent() {
let [a, setA] = React.useState(0);
let [b, setB] = React.useState(0);
// Only re-rendered if `a` changes:
const child1 = React.useMemo(() => <Child a={a} name="a" />, [a]);
// Only re-rendered if `b` changes:
const child2 = React.useMemo(() => <Child b={b} name="b" />, [b]);
return (
<div
onClick={() => {
setA(a + 1);
}}
>
{a % 2 == 0 ? child2 : child1}
</div>
);
}
如果您一直单击 div
,您会看到它仍然打印“rendered b”,即使我们从未更改 b
属性。这是因为 React 每次在 div
中都会收到一个 不同的引用 ,并且不会优化重新渲染 - 就像您只渲染 [=19] 一样=] 没有那个条件和 child1
.
注意:当 React 在同一位置接收到相同的元素引用时会跳过渲染的事实是 known,因此显然 useMemo
技术之所以有效,是因为它出现时渲染优化。
我已经实现了这个组件:
function CardList({
data = [],
isLoading = false,
ListHeaderComponent,
ListEmptyComponent,
...props
}) {
const keyExtractor = useCallback(({ id }) => id, []);
const renderItem = useCallback(
({ item, index }) => (
<Card
data={item}
onLayout={(event) => {
itemHeights.current[index] = event.nativeEvent.layout.height;
}}
/>
),
[]
);
const renderFooter = useCallback(() => {
if (!isLoading) return null;
return (
<View style={globalStyles.listFooter}>
<Loading />
</View>
);
}, [isLoading]);
return (
<FlatList
{...props}
data={data}
keyExtractor={keyExtractor}
renderItem={renderItem}
ListHeaderComponent={ListHeaderComponent}
ListFooterComponent={renderFooter()}
ListEmptyComponent={ListEmptyComponent}
/>
);
}
由于我的 CardList 组件很重,我尝试按照这些 tips.
对其进行优化但是,我认为我应该使用 useMemo
而不是对 renderFooter
使用 useCallback
,以便记住生成的 JSX 而不是方法:
const ListFooterComponent = useMemo(() => {
if (!isLoading) return null;
return (
<View style={globalStyles.listFooter}>
<Loading />
</View>
);
}, [isLoading]);
我说得对吗?
“应该”是见仁见智的事情,但你当然可以。 React 元素是完全可重用的。但是,它不太可能对您的组件产生任何真正的影响,因为创建元素很快并且 renderFooter
只是立即用于已经 运行 的组件(不像 keyExtractor
和 renderItem
,您要将其传递给 FlatList
,因此您希望尽可能使它们稳定,以便 FlatList
可以优化其 re-rendering)。但你当然可以做到。
useMemo
在这里非常明智,因为它会记住结果(JSX)。正如您所说,useCallback
记住了 函数 而不是结果。
如果您想避免昂贵的计算,请使用useMemo
。 useCallback
用于记忆 callback/function.
Official 文档确实有一个将 useMemo
与 JSX 一起使用以避免重新渲染的示例:
function Parent({ a, b }) {
// Only re-rendered if `a` changes:
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// Only re-rendered if `b` changes:
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
}
个人观察:
但更详细地说,似乎并不是 useMemo
这里神奇地阻止了重新渲染,而是你在组件层次结构中的同一位置渲染 相同的引用, 使 react 跳过重新渲染。
这里:
let Child = (props) => {
console.log('rendered', props.name);
return <div>Child</div>;
};
export default function Parent() {
let [a, setA] = React.useState(0);
let [b, setB] = React.useState(0);
// Only re-rendered if `a` changes:
const child1 = React.useMemo(() => <Child a={a} name="a" />, [a]);
// Only re-rendered if `b` changes:
const child2 = React.useMemo(() => <Child b={b} name="b" />, [b]);
return (
<div
onClick={() => {
setA(a + 1);
}}
>
{a % 2 == 0 ? child2 : child1}
</div>
);
}
如果您一直单击 div
,您会看到它仍然打印“rendered b”,即使我们从未更改 b
属性。这是因为 React 每次在 div
中都会收到一个 不同的引用 ,并且不会优化重新渲染 - 就像您只渲染 [=19] 一样=] 没有那个条件和 child1
.
注意:当 React 在同一位置接收到相同的元素引用时会跳过渲染的事实是 known,因此显然 useMemo
技术之所以有效,是因为它出现时渲染优化。