简体   繁体   中英

React Hook : Correct way of using custom hook to handle onClick Event?

As the title said, what is the correct way of using custom hook to handle onClick Event?

This codesandbox application will display a new quote on the screen when user clicks the search button.

function App() {
  const [{ data, isLoading, isError }, doFetch] = useDataApi(
    "https://api.quotable.io/random"
  );

  return (
    <Fragment>
      <button disabled={isLoading} onClick={doFetch}>
        Search
      </button>
      {isError && <div>Something went wrong ...</div>}
      {isLoading ? <div>Loading ...</div> : <div>{data.content}</div>}
    </Fragment>
  );
}

I created a custom hook called useDataApi() which would fetch a new quote from an API. In order to update the quote when the user clicks the button, inside the useDataApi() , I created a handleClick() which will change the value of a click value to trigger re-render. And this handleClick() function will be return back to App()

const useDataApi = initialUrl => {
  const [data, setData] = useState("");
  const [click, setClick] = useState(true);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const handleClick = () => {
    setClick(!click);
  };

  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      try {
        const result = await axios(initialUrl);

        setData(result.data);
      } catch (error) {
        setIsError(true);
      }
      setIsLoading(false);
    };
    fetchData();
  }, [initialUrl, click]);

  return [{ data, isLoading, isError }, handleClick];
};

This is working, however, I don't feel this is the correct solution.

I also tried moving the fetchData() out of useEffect and return the fetchData() , and it works too. But according to the React Doc , it says it is recommended to move functions inside the useEffect .

const useDataApi = initialUrl => {
  const [data, setData] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const fetchData = async () => {
    setIsError(false);
    setIsLoading(true);
    try {
      const result = await axios(initialUrl);
      setData(result.data);
    } catch (error) {
      setIsError(true);
    }
    setIsLoading(false);
  };

  useEffect(() => {
    fetchData();
  }, []);

  return [{ data, isLoading, isError }, fetchData];
};

In addition, for creating these kinds of application, is the way that I am using is fine or there is another correct solution such as not using any useEffects or not create any custom Hook? Thanks

Not sure if this is correct, but here is my solution.

const useDataApi = initialUrl => {
  const [data, setData] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const doFetch = async () => {
    setIsError(false);
    setIsLoading(true);
    try {
      const result = await axios(initialUrl);

      setData(result.data);
    } catch (error) {
      setIsError(true);
    }
    setIsLoading(false);
  };

  return [{ data, isLoading, isError }, doFetch];
};

Btw, don't mutate state directly.

const handleClick = () => {
    setClick(!click);         // don't do this
    setClick(prev => !prev);    // use this
};

Your implementation is fine. We are also using something similar. Hope you find it useful.

 function useApi(promiseFunction, deps, shouldRun=true){ // promisFunction returns promise const [loading, setLoading] = useState(false) const [data, setData] = useState(false) const [error, setError] = useState(false) const dependencies: any[] = useMemo(()=>{ return [...dependencyArray, shouldRun] },[...dependencyArray, shouldRun]) const reload = () => { async function call() { try { setError(null) setLoading(true) const res = await promiseFunction(); } catch (error) { setError(error) } finally { setLoading(false) } } call(); } useEffect(() => { if(,shouldRun) return setResult(null) //no stale data reload() }, dependencies) return {loading, error, data, reload: setState: setData} }

Below code will provide some idea about how to use it.

 function getUsersList(){ return fetch('/users') } function getUserDetail(id){ return fetch(`/user/${id}`) } const {loading, error, data } = useApi(getUsersList, [], true) const {loading: userLoading, error: userError, data: userData} = useApi(()=>getUserDetail(id), [id], true)

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