How can I fetch data from API using an ID passed to a function when clicking a button

I have used hooks in index.js to fetch data from an API. The data is displayed in rows and every row has a button with onClick={() => EditExercise(item.idexercise)}. EditExercise is a function in a .jsx file that receives the id and fetches a single item from the same API.

function EditExercise(id){
    const [error, setError] = useState(null);
    const [isLoaded, setIsLoaded] = useState(false);
    const [item, setItem] = useState([]);

    useEffect(() => {
        fetch("http://localhost:5000/Exercises/GetEditId?id=${id}")
        .then(res => res.json())
        .then(
            (result) => {
                setIsLoaded(true);
                setItem(result);
            },
            (error) => {
                setIsLoaded(true);
                setError(error);
        }
        )
    }, [])

    if (error) {
    return <div>Error: {error.message}</div>;
  } else if (!isLoaded) {
    return <div>Loading...</div>;
  } else {
    return (
        <input type="text" id="name">{item.exerciseName}</input>
        <input type="text" id="desc">{item.instructions}</input>
    );
  }
}

I’m trying to return a couple of input texts to display data like "name" and "description" using that ID. The error is indicating that I’m using an invalid hook call.

>Solution :

The error message you’re seeing is likely due to using the useState and useEffect hooks inside the EditExercise function. React hooks can only be used at the top level of a functional component or another hook.

To fix this error, you can move the EditExercise function to its own component and pass the id as a prop. Here’s an example:

function EditExercise({ id }) {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [item, setItem] = useState([]);

  useEffect(() => {
    fetch(`http://localhost:5000/Exercises/GetEditId?id=${id}`)
      .then(res => res.json())
      .then(
        (result) => {
          setIsLoaded(true);
          setItem(result);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      )
  }, [id])

  if (error) {
    return <div>Error: {error.message}</div>;
  } else if (!isLoaded) {
    return <div>Loading...</div>;
  } else {
    return (
      <>
        <input type="text" id="name" value={item.exerciseName} />
        <input type="text" id="desc" value={item.instructions} />
      </>
    );
  }
}

function Row({ item }) {
  const handleEdit = () => {
    EditExercise(item.idexercise);
  }

  return (
    <tr>
      <td>{item.exerciseName}</td>
      <td>{item.instructions}</td>
      <td>
        <button onClick={handleEdit}>Edit</button>
      </td>
    </tr>
  );
}

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch("http://localhost:5000/Exercises/Get")
      .then(res => res.json())
      .then(
        (result) => {
          setData(result);
        },
        (error) => {
          console.error(error);
        }
      )
  }, [])

  return (
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Description</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        {data.map(item => (
          <Row key={item.idexercise} item={item} />
        ))}
      </tbody>
    </table>
  );
}

In this example, the EditExercise function has been moved to its own component and takes the id as a prop. The Row component handles the rendering of each row and passes the id to the EditExercise component when the Edit button is clicked.

Also note that in the EditExercise component, the useEffect hook now has [id] as its second argument. This means that the effect will only run when the id prop changes, which ensures that the component fetches the correct data when it’s rendered with a new id. Additionally, the input elements have been updated to include the value attribute to show the fetched data.

Leave a Reply