Search code examples
reactjsreact-hooksreact-custom-hooks

How to use the custom hook on click event which contains useEffect and useReducer?


I am using custom hook which contains useEffect and useReducer for API calls and I call this custom hook on button click and I got this error (React Hook "useAuth" is called in function "handleClick" that is neither a React function component nor a custom React Hook function). Code is below

useAuth.js

import axios from 'axios'
import {useEffect,useReducer} from 'react'

const ACTION = {
 LOADING:'loading',
 SUCCESS:'success',
 ERROR:'error'
}

function reducer(state,action) {
 switch (action) {
    case ACTION.LOADING:
        return {loading:true,data:[]}
    case ACTION.SUCCESS:
        return {...state,loading:false,data:action.payload.data}
    case ACTION.ERROR:
        return {...state,loading:false,data:[],error:action.payload.error}
    default:
        break;
  }
}   
function useAuth (data) {
 const [state, dispatch] = useReducer(reducer, {data:[],loading:true})

 useEffect(() => {
    dispatch({type:ACTION.LOADING})
    const getData = async ()=>{
      try {
          const response = await axios.post('https://expamle.com',data)
          dispatch({type:ACTION.SUCCESS,payload:{data:response.data.data}})
      } catch (error) {
          dispatch({type:ACTION.ERROR,payload:{data:error.response}})
      }
    }
  getData()
 }, [])
 return state
}
export default useAuth

app.js

import logo from './logo.svg';
import './App.css';
import useAuth from './useAuth'

function App() {
 // const {loading,data,error} = useAuth()

 const handleClick = () => {
   const {loading,data,error} = useAuth() // how to use custom hook on click event
 }

 return (
  <div className="App">
    <button onClick={handleClick}></button>
  </div>
 );
}

export default App;

Solution

  • Ideally, it should be written like this

    import logo from './logo.svg';
    import './App.css';
    import useAuth from './useAuth'
    
    function App() {
     const { loading , data ,error, dispatch} = useAuth()
    
     const handleClick = () => {
       dispatch({type:'USER_CLICKED'})
       console.log('check for your data', data)
     }
    
     return (
      <div className="App">
        <button onClick={handleClick}></button>
      </div>
     );
    }
    

    In your useAuth hook you should have a flag that becomes true upon button click

    const ACTION = {
     LOADING:'loading',
     SUCCESS:'success',
     ERROR:'error'
    }
    
    function reducer(state,action) {
     switch (action) {
        case ACTION.USER_CLICK:
            return {...state, userClicked: true}
        case ACTION.LOADING:
            return {loading:true,data:[]}
        case ACTION.SUCCESS:
            return {...state,loading:false,data:action.payload.data}
        case ACTION.ERROR:
            return {...state,loading:false,data:[],error:action.payload.error}
        default:
            break;
      }
    }   
    
    function useAuth(data) {
      const [state, dispatch] = useReducer(reducer, { data: [], loading: true, userClicked: false });
     
      useEffect(() => {
        if (state.userClicked) {
          dispatch({ type: ACTION.LOADING });
          const getData = async () => {
            try {
              const response = await axios.post("https://expamle.com", data);
              dispatch({
                type: ACTION.SUCCESS,
                payload: { data: response.data.data },
              });
            } catch (error) {
              dispatch({ type: ACTION.ERROR, payload: { data: error.response } });
            }
          };
          getData();
        }
      }, [userClicked]);
      return { state, dispatch }; 
    }
    export default useAuth;