skip to Main Content

loading is being set and passed into props but the loader fires on all instances of item when onClick is pressed on any button. How do I only have the loader run on the button that is pressed?

List.map((item, i) => {
  return(
<Button onClick={(e) => !props.loading && e.stopPropagation()}>
  Save {props.loading && (<CircularProgress/>)}
</Button>
  );
});

3

Answers


  1. You need to specify a way to get a handle on the unique item in the loop. Lets say the List is an array of items in the shape of [{ id: 1, name: "one"},{ id: 2, name: "Two"}]

    Insead of props.loading, you need to specfy the ID in loading, not a boolean.

    So for example in your component

    Then in your map:

    Save {props.loading === item.id && ()}

    Login or Signup to reply.
  2. As might have realised the issue is that you are using the same prop for all the buttons.

    One way to avoid this is changing the loading prop type from boolean to an unique identifier (so you know which list item is loading). Assuming you set the loading on click and your item has an unique id you could do something like:

      List.map((item, i) => {
        return (
          <Button
            onClick={(e) => {
              if (props.loading === item.id) return // or props.loading === index or any unique identifier
              e.stopPropagation()
              prop.setLoading(item.id) // or prop.setLoading(index) or any unique identifier
            }}
          >
            Save {props.loading === item.id && <CircularProgress />} // or props.loading === index or any unique identifier
          </Button>
        )
    });
    

    Please note that you can call the setLoading outside of this function. This is only for demonstration purposes. You can call it where you need and where it makes more sense.

    You can also use the any other unique identifier instead of id (as shown in the comments).

    But for the index solution, if your list is filtrable or the index of one element might change for any reason (for example: list might trim or an element might be added/removed) is it not recommended to use index prop since it might cause unexpected behaviour (you would need to recalculate the loading prop each time an element changes it’s index). So I would suggest any other unique identifier from List array if possible.

    Login or Signup to reply.
  3. Instead of a boolean variable for loading in the parent component, you must use an array of Ids to render loading in each button. Here is an example:
    You can check this code in the code sandbox here.

    import "./styles.css";
    import CircularProgress from "@mui/material/CircularProgress";
    import { useState } from "react";
    
    const items = ["1", "2", "3", "4", "5", "6"];
    
    export default function App() {
      const [loading, setLoading] = useState([]);
    
      return (
        <div className="App">
          {items.map((item) => (
            <button
              key={item}
              id={item}
              onClick={(e) => {
                setLoading([...loading, e.target.id]);
              }}
            >
              {loading.includes(item) ? <CircularProgress /> : item}
            </button>
          ))}
        </div>
      );
    }
    

    this is a simple example that I have implemented. If the user clicks on a button, only that button will render a loading spinner. You can implement it in many ways. You can also add logic to remove loading if the user clicks on the loading button by checking whether the item exists in the state. If it is, You can simply remove it from the loading list.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search