I have a map function displaying items:
{data.map((item, index) => ())}
Within this function I would like to have an onClick that conditionally displays a loading state when loading is true.
const [loading, setLoading] = useState(false)
{data.map((item, index) => (
<span
onClick={() =>
handleDelete()}>
{loading ? (
<LoadingSpinner />
) : (
<TrashIcon />)}
</span>
))}
When I do this it causes all items in the list to display the loading state, not just the item clicked on.
Is there a way I can achieve this using the index?
CodePudding user response:
All your map items are listening to a single boolean state value, instead, you can keep track of the index of the clicked item, or if you want to be able to click and show the loading state for multiple items you can use an array or Set.
Below is an approach using Set
const [loadingIndices, setLoadingIndices] = useState(new Set());
{data.map((item, index) => (
<span
onClick={() =>
handleDelete(index)}>
{loadingIndices.has(index) ? (
<LoadingSpinner />
) : (
<TrashIcon />)}
</span>
))}
Now in your handleDelete function, you can add index of the clicked element to the set in state.
handleDelete = (selectedIndex) => {
setLoadingIndices((prev) => new Set([...prev, selectedIndex]));
// ...
// ...
// And to remove the element from loading state
setLoadingIndices((prev) => {
const updated = new Set(prev);
updated.delete(selectedIndex);
return updated;
});
};
CodePudding user response:
You should move your state in to the items instead of the parent. Updating state inside the parent component will cause all children to re-render.
const childComponent = (props) => { // you can pass the main loading state as a starting point
const [loading,setLoading] = useState(props.loading) // initial loader state
if(loading) return <LoadingSpinner />
return (
<button onClick={()=> setLoading(prev => !prev)} />
)}
you can then mutate this to display different elements using if statements like above.
CodePudding user response:
If you don't mind the re-render of children, You can use this index logic.
const [loading, setLoading] = useState(null);
const handleDelete = (index) => {
setLoading(index);
}
{data.map((item, index) => (
<span
onClick={() =>
handleDelete(index)}>
{loading === index ? (
<LoadingSpinner />
) : (
<TrashIcon />)}
</span>
))}