javascriptreactjsreact-hooksreact-custom-hooks

Hooks can only be called inside of the body


I created a custom hook usePostCategory to post a new category. The hook is called from another component, but when I try to do it, I receive an error: " Invalid hook call. Hooks can only be called inside of the body of a function component".

Just wondering if I call the hook in the right way, since I get confuse because it accepts parameters and also return something

usePostCategory hook:

  export const usePostCategory = async (categoryName, id, catId) => {
  const data = { name: categoryName, a_id: id, cat_id: catId };
  
  const [message, setMessagge] = useState();

 
  try {
    const response = await axios.post(`url`, data, {
      headers: {... 
      },
    });
    if (response.status === 201) {
      
    }
  } catch (err) {
    
  }
  return { message };
};

This is the parent component where I call the hook:

     import {usePostCategory } from "../../../api/bagAPI/fetchBag";

      function Foo(value, id, catID) {
        const  message  = usePostCategory(value, id, catID);
        return message;
      }
    const handleAddCategory = () => {
        setOpenAddCategory(true);
      };

       <Fab
          onClick={handleAddCategory}
          text="Category"
          icon={<AddIcon/>}>
       </Fab>

     <CustomFormDialog
        title="Add category"
        onClick={Foo}
        id={id}
        editCatId={currentCatId}
      />

**and also my dialog:**

    const CustomFormDialog = ({ open, onClose, title, id, onClick, editCatId }) => {}
               <Button
                onClick={() => {
                  onClick(value, id, editCatId);
                }}>

I have been trying different way, but I always get the same error:


     const { message } = usePostCategory();
    <CustomFormDialog
            title="Add category"
            onClick={usePostCategory}
            id={id}
            editCatId={currentCatId}
          />

Solution

  • Hooks can't be async, either.

    You've got yourself quite the conundrum here, there's an understanding of async programming and react state update that you need here as well

    I'll give it a shot:

    What you almost always want in this circumstance is a hook that returns a function that the parent component can call as necessary.

    const usePostCategory = () => {
    
       const [message, setMessagge] = useState();
       const [fetching,setFetching] = useState()
       const [error,setError] = useState()
    
       return {
         message,
         fetching,
         error,
         mutate: async (categoryName, id, cat_id) => {
           setFetching(true)
           const data = { name: categoryName, a_id: id, cat_id: catId };
           try {
              const response = await axios.post(...)
              setMessage(...)
           } catch {
              setError(...)
           } finally {
              setFetching(false)
           }
         }
    
       
    
    }
    
    const MyComponent = () => {
        const { message, error, fetching, mutate } = usePostCategory()
    
        // if fetching is true, show a loader
    
        // if message is true, show a toast?
     
        // if error is true, show a toast?
    
        return (
           <SomeModal onConfirm={() => mutate(...your args)}/>
        )
     
    }
    

    This pattern is stolen pretty directly from SWR, ReactQuery or RTKQuery and I highly encourage anybody to those one of those librar