简体   繁体   中英

Using A React Custom Hook With Side Effects For Event Handling (e.g. OnClick)

I am running into the following problem while trying to use custom hooks to handle an OnClick event:

When OnClick is clicked, an API request should be sent, and the state should be updated based on the response.

One way to handle this is to simply create an async function inside (or outside) my component that fetches the data and does the processing. This can work, but this approach will not allow me to use my custom hooks (eg useFetch). It will also not allow me to use useEffect, which is the recommended way to handle side effects.

The other approach is to use a custom hook to handle the click. I would call my custom hook at the beginning of my functional component's body, then the custom hook can return some function that can be used as an OnClick handler. But that function will not be able to utilize useEffect (since useEffect can only be called from a hook), so I am not sure how to approach this.

It seems like a common scenario, but I could not find any good solution so far.

Thanks in advance

Well, your useFetch should not care about buttons click or state management. useFetch should only fetch the data from the API and return the response. I highly recommend react-query , but if you cannot use it, you can at least inspire from their API.

For your custom hooks useFetch you can do something like this:

useFetch.js

import { useEffect, useRef, useState } from "react";
const useFetch = (isTrig) => {
  const isEmpty = (data) => {
    return Object.keys(data).length === 0;
  };
  const [pokemons, setPokemons] = useState(null);
  const firstUpdate = useRef(true);
  const offset = useRef(0); // this is only to show you that you make a new fectch on every onClick call
  useEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;

      return;
    }
    fetch(
      `https://pokeapi.co/api/v2/pokemon/?limit=10&offset=${offset.current}`
    )
      .then((response) => response.json())
      .then((data) => (isEmpty(data) ? null : data.results))
      .then((resPokemon) => setPokemons(resPokemon));
    offset.current += 10;
  }, [isTrig]);

  return pokemons;
};

export default useFetch;


in this custom hook you are using 3 hooks from hook api useEffect, useState, and useRef.
The isTrig in usffect parameter is in this example a boolean but you can pass whatever you want as long as the value change to "trig" the request, for example you can pass an id and insert it to the request. useRef is used to avoid the request to be launch when you pass to a variable in your component. You can call this useFetch like this in you component:
App.js

import "./styles.css";
import { useState } from "react";
import useFetch from "./useFetch";
export default function App() {
  const [isTrig, setIsTrig] = useState(false);
  const pokemons = useFetch(isTrig);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={() => setIsTrig((prev) => !prev)}>Fetch Pokemon</button>
      {pokemons && pokemons.map((p) => <div key={p.name}>{p.name}</div>)}
    </div>
  );
}

here you can find a working example:
编辑 dazzling-lewin-dtviz

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM