I have following tag selection:
It works fine but the problem is it hangs for a few seconds after I'm able to select another Tag. This is my code:
let selected = []
const TagScreen = () => {
const [allInterests, setAllInterests] = useState(["interst1", "interst2", "interst3", "interst4", "interst5", "interst6", "interst7", "interst8", "interst9", "interst10", ...
const [interests, setInterests] = useState([])
const fire1 = (item) => {
selected.unshift(item)
setInterests([...interests, item])
}
const fire2 = (item) => {
let index = selected.indexOf(item);
setInterests([...interests, item])
if(index!=-1){
selected.splice(index, 1);
}
}
return (
<View>
<View style={{display: "flex", flexWrap: "wrap", flexDirection: "row"}}>
{allInterests.map((item) => {
return(
<View key={Math.random()}>
{!selected.includes(item) ?
<PressableBackground onPress={() => !selected.includes(item) ? fire1(item) : fire2(item)} >
<View style={{margin: 2.5,}}>
<Tag>
<Text style={{color: colors.text}}>{item}</Text>
</Tag>
</View>
</PressableBackground>
:
<PressableBackground onPress={() => !selected.includes(item) ? fire1(item) : fire2(item)} >
<View style={{margin: 2.5,}}>
<Tag color={colors.card}>
<Text style={{color: colors.background}}>{item}</Text>
</Tag>
</View>
</PressableBackground>
}
</View>
)
})}
</View>
</View>
);
}
export default TagScreen;
I also tried React.Callback or React.useMemo. Sadly it didn't work
CodePudding user response:
It hangs because you are re-rendering every tag on the screen and it takes long time. useCallback and useMemo does nothing in your case because they only memoizes your function / function result, they won't stop your content from re-rendering without React.memo
. You can seperate your tag component and its logic from TagScreen to save rendering.
You also need to replace selected array with useState to prevent it from getting unsynchronized.
Another problem is, you're using Math.random() as a key for your tag items. You should never use random strings as key. https://stackoverflow.com/a/29813867/5793132
const TagScreen = () => {
const [allInterests, setAllInterests] = useState(["interst1", "interst2", "interst3", "interst4", "interst5", "interst6", "interst7", "interst8", "interst9", "interst10"]);
// interests is unused?
const [interests, setInterests] = useState([]);
const [selected, setSelected] = useState([]);
// Added useCallback to memoize function because this is going to be passed to a memoized component.
// Replaced direct state changes with state action to remove interests and selected dependencies
const fire1 = React.useCallback((item) => {
setSelected((prev) => [item, ...prev]);
setInterests((prev) => [...prev, item]);
}, []);
// Added useCallback to memoize function because this is going to be passed to a memoized component.
// Replaced direct state changes with state action to remove interests and selected dependencies
const fire2 = React.useCallback((item) => {
setInterests((prev) => [...prev, item]);
setSelected((prev) => {
let index = prev.indexOf(item);
if (index !== -1) {
prev.splice(index, 1);
}
return [...prev];
});
}, []);
return (
<View>
<View style={{display: "flex", flexWrap: "wrap", flexDirection: "row"}}>
{allInterests.map((item) => {
return (
// Math.random() replaced with item
// Created isSelected prop because you should only change props when really necessary while using React.memo
// because React.memo will re-render your component when a prop changes.
// passing other required props.
<TagItem key={item} item={item} isSelected={selected.includes(item)} fire1={fire1} fire2={fire2} />
)
})}
</View>
</View>
);
};
// TagItem seperated from TagScreen component.
const TagItem = React.memo(({item, isSelected, fire1, fire2}) => {
return (
<View>
{isSelected ? (
<PressableBackground onPress={() => fire2(item)} >
<View style={{margin: 2.5,}}>
<Tag>
<Text style={{color: colors.text}}>{item}</Text>
</Tag>
</View>
</PressableBackground>
) : (
<PressableBackground onPress={() => fire1(item)} >
<View style={{margin: 2.5,}}>
<Tag color={colors.card}>
<Text style={{color: colors.background}}>{item}</Text>
</Tag>
</View>
</PressableBackground>
)}
</View>
)
});
export default TagScreen;