简体   繁体   中英

React higher order function to return hook

Currently, I have a custom fetching data hook written in javascript and it works

import {useState, useEffect} from 'react';

const useApi = apiName => id => {
  const [response, setResponse] = useState();
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);
  const fetching = async () => {
    setLoading(true);
    const data = await fetch(`/api/${apiName}${id ? `/${id}` : ""}`)
      .then((x) => x.json())
      .catch((error) => setError(error));

    setResponse(data);
    setLoading(false);
  };

  useEffect(() => {
    fetching();
  }, [id]);

  return { response, loading, error };
};

Then I can use pass in what api I want to call to get the hook. For examples:

const useCustomer = useApi("customer")
const useHello = useApi("hello")
.....
const {response, loading, error} = useCustomer("id_1")

It works fine.

Then, I try to convert to typescript

const useApi = (apiName:string) => (id?:string) => {
  const [response, setResponse] = useState({})
.......
}

and eslint complains that

React Hook "useState" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function

I would like to know whats wrong with this approach, I know I can have something like:

const useApi = (apiName:string, id?:string) => {} 

or disable the eslint(react-hooks/rules-of-hooks)

But just curious about whats the potential problems having higher order function of hook since it actually return the response.

Thanks

When you name you function with prefix hooks, eslint thinks of it as a custom hook according to the general convention. Now that implements useState in a nested function which is why it gives you an error

The best way to write the above code is to not use currying function but pass in the apiName as a param directly

const useApi = (apiName, id) => {
  const [response, setResponse] = useState();
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);
  const fetching = async () => {
    setLoading(true);
    const data = await fetch(`/api/${apiName}${id ? `/${id}` : ""}`)
      .then((x) => x.json())
      .catch((error) => setError(error));

    setResponse(data);
    setLoading(false);
  };

  useEffect(() => {
    fetching();
  }, [id]);

  return { response, loading, error };
};

and use it like

.....

const {response, loading, error} = useApi("customer","id_1");

PS Hooks are meant to be an alternative to HOC's and there is no point writing a hook if you use it as an HOC itself

There's a much easier way if you don't need the id variable to be in the hook. The reason why you get the warning is because your hooks are in your CB instead of your root function.

Correct Example:

const useApi = (apiName:string) => {
  const [response, setResponse] = useState({});

  return (id?: string) => {
    .......
  };
}

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