Skip to content

Instantly share code, notes, and snippets.

@williamjayjay
Created July 16, 2025 15:09
Show Gist options
  • Save williamjayjay/a183c555115e90eea967cb4edd61c39f to your computer and use it in GitHub Desktop.
Save williamjayjay/a183c555115e90eea967cb4edd61c39f to your computer and use it in GitHub Desktop.
performance, react memo multiselect
import React, { memo, useCallback, useState } from 'react';
import { FlatList, Text, Button, View, StyleSheet, Pressable } from 'react-native';
// Interface para os dados de cada item da lista
interface ItemData {
id: string;
selected: boolean; // Indica se o item está selecionado
}
// Interface para as propriedades do componente Item
interface ItemProps {
id: string;
selected: boolean; // Propriedade para indicar se o item está selecionado
onPress: (id: string) => void; // Função para lidar com o toque curto
onLongPress: (id: string) => void; // Função para lidar com o toque longo
}
// Componente Item memoizado para otimização de performance
const Item: React.FC<ItemProps> = memo(({ id, selected, onPress, onLongPress }) => {
console.count(`Renderizações do Item ${id}`);
return (
<Pressable
onPress={() => onPress(id)} // Chama a função de toque curto
onLongPress={() => onLongPress(id)} // Chama a função de toque longo
style={({ pressed }) => [
styles.item,
selected && styles.selectedItem, // Aplica estilo se o item estiver selecionado
pressed && styles.pressedItem, // Feedback visual ao pressionar
]}
>
<Text style={styles.itemText}>Item {id}</Text>
{selected && <Text style={styles.selectedIndicator}>✓</Text>}
</Pressable>
);
});
// Componente principal do aplicativo
const App: React.FC = () => {
// Estado para o contador, para testar re-renderizações da lista
const [count, setCount] = useState<number>(0);
// Estado para os itens da lista, incluindo seu estado de seleção
const [items, setItems] = useState<ItemData[]>(
Array.from({ length: 100 }, (_, i) => ({ id: i.toString(), selected: false }))
);
// Estado para controlar se o modo de seleção múltipla está ativo
const [selectionMode, setSelectionMode] = useState<boolean>(false);
// Calcula o número de itens selecionados dinamicamente
const selectedCount = items.filter(item => item.selected).length;
// Função useCallback para lidar com o toque curto em um item
const handleItemPress = useCallback((id: string) => {
if (selectionMode) {
// Alterna o estado 'selected' do item
setItems(prevItems =>
prevItems.map(item =>
item.id === id ? { ...item, selected: !item.selected } : item
)
);
} else {
console.log(`Item ${id} clicado normalmente`);
}
}, [selectionMode]);
// Função useCallback para lidar com o toque longo em um item
const handleItemLongPress = useCallback((id: string) => {
setSelectionMode(true);
setItems(prevItems =>
prevItems.map(item =>
item.id === id ? { ...item, selected: true } : item
)
);
}, []);
// Função useCallback para limpar apenas os itens selecionados
const clearSelection = useCallback(() => {
setSelectionMode(false);
// Atualiza apenas os itens que estão selecionados
setItems(prevItems =>
prevItems.map(item =>
item.selected ? { ...item, selected: false } : item
)
);
}, []);
// Função useCallback para renderizar cada item na FlatList
const renderItem = useCallback(
({ item }: { item: ItemData }) => (
<Item
id={item.id}
selected={item.selected}
onPress={handleItemPress}
onLongPress={handleItemLongPress}
/>
),
[handleItemPress, handleItemLongPress]
);
return (
<View style={styles.container}>
<View style={styles.controls}>
<Button
title="Incrementar Contador"
onPress={() => setCount(count + 1)}
color="#2196F3"
/>
<Text style={styles.counter}>Contador: {count}</Text>
{selectionMode && (
<Button
title={`Limpar Seleção (${selectedCount})`}
onPress={clearSelection}
color="red"
/>
)}
</View>
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={(item: ItemData) => item.id}
extraData={selectionMode}
/>
</View>
);
};
// Estilos para os componentes
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 50,
backgroundColor: '#f0f0f0',
},
controls: {
padding: 15,
borderBottomWidth: 1,
borderBottomColor: '#ddd',
backgroundColor: '#fff',
alignItems: 'center',
marginBottom: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 3,
},
item: {
padding: 18,
borderBottomWidth: 1,
borderBottomColor: '#eee',
backgroundColor: '#fff',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginHorizontal: 10,
marginVertical: 4,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 1,
elevation: 1,
},
selectedItem: {
backgroundColor: '#e0f7fa',
borderColor: '#00bcd4',
borderWidth: 2,
},
pressedItem: {
opacity: 0.7,
},
itemText: {
fontSize: 17,
color: '#333',
fontWeight: '500',
},
selectedIndicator: {
fontSize: 22,
color: '#00bcd4',
fontWeight: 'bold',
},
counter: {
fontSize: 22,
marginVertical: 15,
fontWeight: 'bold',
color: '#555',
},
});
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment