He implementado este componente:
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} /> ); }Como mi componente CardList es pesado, he intentado optimizarlo siguiendo estos consejos .
Pero, creo que en lugar de usar useCallback para renderFooter , debería usar useMemo , para memorizar el JSX resultante y no el método:
const ListFooterComponent = useMemo(() => { if (!isLoading) return null; return ( <View style={globalStyles.listFooter}> <Loading /> </View> ); }, [isLoading]);¿Estoy en lo correcto?
Si desea evitar cálculos costosos, use useMemo . useCallback es para memorizar una devolución de llamada/función .
Los documentos oficiales tienen un ejemplo del uso de useMemo con JSX para evitar volver a renderizar:
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} </> ) }Observación personal:
Pero para ser más detallados, parece que useMemo aquí no evita mágicamente la renderización, sino el hecho de que renderiza la misma referencia en el mismo lugar en la jerarquía de componentes , hace que la reacción se salte la renderización.
Aquí:
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> ); } Si sigue haciendo clic en el div , puede ver que todavía se imprime "b renderizado" a pesar de que nunca cambiamos la propiedad b . Esto se debe a que react recibe una referencia diferente cada vez dentro del div y no optimiza el renderizado, como lo haría si simplemente hubiera renderizado solo child2 sin esa condición y child1 .
Nota: Se conoce el hecho de que reacciona salta el renderizado cuando recibe la misma referencia de elemento en el mismo lugar, por lo que aparentemente la técnica useMemo funciona debido a eso cuando se trata de optimización de renderizado.
"Debería" es una cuestión de opinión, pero ciertamente se puede . Los elementos React son totalmente reutilizables. Sin embargo, no es probable que haga una diferencia real en su componente, ya que crear los elementos es rápido y renderFooter se usa inmediatamente en un componente que ya se está ejecutando (a diferencia keyExtractor y renderItem , que está pasando a FlatList , por lo que desea hágalos estables donde sea posible para que FlatList pueda optimizar su renderizado). Pero ciertamente puedes hacer eso.
useMemo es perfectamente sensato aquí ya que memoriza el resultado (el JSX). Como dices, useCallback memoriza la función, no el resultado.