i am trying to understand react memo and i created a simple interface in my react native app. the app consists of two elements:
MainApp.tsx -> controls the list of the user
User.tsx -> displays user object
my plan is to have all user information displayed on first render and each user should have some sort of “update” button which would case it to re-render. The user would be passed to the list item component along with a description. If the description changes, the whole list should be re-rendered. The current implementation looks like this:
mainapp:
// MainApp component
import React, { useState } from 'react';
import { StyleSheet, Button, SafeAreaView, FlatList } from 'react-native';
import User from './User';
export interface IUser {
name: string;
id: number;
age: number;
}
const initialUsers: IUser[] = [
{ id: 1, name: 'Ivan', age: 20 },
{ id: 2, name: 'Elena', age: 25 },
{ id: 3, name: 'John', age: 30 },
];
export const MainApp = () => {
const [users, setUsers] = useState<IUser[]>(initialUsers);
const [description, setDescription] = useState<string>(
'A passionate developer',
);
const updateUserAge = (userId: number) => {
setUsers(
users.map(user =>
user.id === userId
? { ...user, age: Math.floor(Math.random() * (100 - 20 + 1)) + 20 }
: user,
),
);
};
const updateDescription = () => {
setDescription(
(Math.floor(Math.random() * (100 - 20 + 1)) + 20).toString(),
);
};
return (
<SafeAreaView style={styles.container}>
<FlatList
data={users}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => (
<User
user={item}
description={description}
onUpdateAge={updateUserAge}
/>
)}
/>
<Button onPress={updateDescription} title="Update Description" />
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default MainApp;
user.tsx
import React, { memo } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { IUser } from './MainApp';
interface UserProps {
user: IUser;
description: string;
onUpdateAge: (userId: number) => void;
}
export const User = memo(
({ user, description, onUpdateAge }: UserProps) => {
console.log('Rendering User', user.name);
const handleUpdateAge = () => {
onUpdateAge(user.id);
};
return (
<View style={styles.container}>
<Text>Name: {user.name}</Text>
<Text>Age: {user.age}</Text>
<Text>Description: {description}</Text>
<Button onPress={handleUpdateAge} title="Update Age" />
</View>
);
},
(prevProps, nextProps) => {
return (
prevProps.user.age === nextProps.user.age &&
prevProps.description === nextProps.description
);
},
);
const styles = StyleSheet.create({
container: {
margin: 10,
padding: 10,
backgroundColor: '#eee',
},
});
export default User;
since the object reference stays the same, i specify what props to compare. When i click on the first element i get:
LOG Rendering User Ivan
which is correct and the whole list was not re-rendered, only one item is updated.
however, if i click on another list item after that i get this:
LOG Rendering User Ivan
LOG Rendering User Elena
For some reason two list items were updated and it keeps going if i click on another users. Can you help me understand why the list items are re-rendered?