Home > Blockchain >  React Hook "useAxios" is called in function that is neither a React function component nor
React Hook "useAxios" is called in function that is neither a React function component nor

Time:05-23

I created a custom useAxios hook to make api calls.

I am trying to use this in my login component as below.

import { useContext, useState } from 'react';
import './login.scss';
import { useNavigate } from 'react-router-dom';
import { useAxios } from '../../api/use-axios';
import ApiConfig from '../../api/api-config';
import { AuthContext } from '../../context/AuthContext';

const LOGIN_AXIOS_CONFIG = ApiConfig.AUTH.LOGIN;

const Login = () => {
  const [loginError, setLoginError] = useState('');
  const [phone, setPhone] = useState('');
  const [password, setPassword] = useState('');

  const navigate = useNavigate();
  const { dispatch } = useContext(AuthContext);

  const handleLogin = (e) => {
    e.preventDefault();
    LOGIN_AXIOS_CONFIG.data = {
      phone,
      password,
    };
    const { response: loginData, error } = useAxios(LOGIN_AXIOS_CONFIG);
    if (error) {
      setLoginError(error);
    } 
    if (loginData) {
        dispatch({ type: 'LOGIN', payload: loginData });
        navigate('/');
    }
  };
  return (
    <div className="login">
      <div className="logo">
        <h1>LOGO</h1>
        <h3>LOGO DESCRIPTION</h3>
      </div>
      <form onSubmit={handleLogin}>
        <input type="number" placeholder="phone" onChange={(e) => setPhone(e.target.value)} />
        <input type="password" placeholder="password" onChange={(e) => setPassword(e.target.value)} />
        <button type="submit">Submit</button>
        {loginError && <span>{loginError}</span>}
      </form>
    </div>
  );
};

export default Login;

use-axios.js

import { useState, useEffect } from 'react';
import axios from 'axios';

export const useAxios = (axiosParams) => {
  const [response, setResponse] = useState(undefined);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(true);

  const fetchData = async (params) => {
    try {
      const result = await axios.request({
        ...params,
        method: params.method || 'GET',
        headers: {
          accept: 'application/json',
          authorization:
            'Bearer my-token',
        },
      });
      console.log(result.data);
      setResponse(result.data);
    } catch (error) {
      setError(error);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData(axiosParams);
  }, [axiosParams]); // execute once only

  return { response, error, loading };
};

Is react seeing use prefix and giving error?

How can I fix this?

CodePudding user response:

useAxios as you designed it, is a hook. And a hook, should follow some rules, known as Rules of Hooks, and here is an overview:

It should only get called a the to level of a React component or at the top level of a hook, not in a function inside a hook or a component like you did, not inside any other block, like if-else...

An approche for your case may be something like this:

import { useContext, useState, useEffect } from 'react';
import './login.scss';
import { useNavigate } from 'react-router-dom';
import { useAxios } from '../../api/use-axios';
import ApiConfig from '../../api/api-config';
import { AuthContext } from '../../context/AuthContext';

const LOGIN_AXIOS_CONFIG = ApiConfig.AUTH.LOGIN;

const Login = () => {
  const [loginAxioConfig, setLogicAxiosConfig]=useState(null);
  const [loginError, setLoginError] = useState('');
  const [phone, setPhone] = useState('');
  const [password, setPassword] = useState('');
  const { response: loginData, error } = useAxios(loginAxioConfig);
  
  const navigate = useNavigate();
  const { dispatch } = useContext(AuthContext);

  const handleLogin = (e) => {
    e.preventDefault();
    setLogicAxiosConfig({...LOGIN_AXIOS_CONFIG, data = {
      phone,
      password,
    } });
    
  };
  
  useEffect(()=>{
   if (error) {
      setLoginError(error);
   } 
  },[error]);

   useEffect(()=>{
     if (loginData) {
      dispatch({ type: 'LOGIN', payload: loginData });
      navigate('/');
     }
  },[loginData])
  return (
    <div className="login">
      <div className="logo">
        <h1>LOGO</h1>
        <h3>LOGO DESCRIPTION</h3>
      </div>
      <form onSubmit={handleLogin}>
        <input type="number" placeholder="phone" onChange={(e) => setPhone(e.target.value)} />
        <input type="password" placeholder="password" onChange={(e) => setPassword(e.target.value)} />
        <button type="submit">Submit</button>
        {loginError && <span>{loginError}</span>}
      </form>
    </div>
  );
};

export default Login;
import { useState, useEffect } from 'react';
import axios from 'axios';

export const useAxios = (axiosParams) => {
  const [response, setResponse] = useState(undefined);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(true);

  const fetchData = async (params) => {
    try {
      const result = await axios.request({
        ...params,
        method: params.method || 'GET',
        headers: {
          accept: 'application/json',
          authorization:
            'Bearer my-token',
        },
      });
      console.log(result.data);
      setResponse(result.data);
    } catch (error) {
      setError(error);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    if(!axiosParams) return;
    fetchData(axiosParams);
  }, [axiosParams]); // execute once only

  return { response, error, loading };
};

CodePudding user response:

You need to make a slight change to your custom hook useAxios since hooks can be invoked only in the body of the component and not conditionally. I add a param to that hook to handle the auto-fetch. In this case you don't want it to trigger automatically the fetch, but imperatively, so just return the fetcher function from the hook and use it imperatively. It would be better not to couple the hook btw and create two separate hooks, something like useAxiosOnMount, useAxiosOnAction. For simplicity I'll just edit your useAxios hook here:


const Login = () => {
  const [loginError, setLoginError] = useState('');
  const [phone, setPhone] = useState('');
  const [password, setPassword] = useState('');

  const navigate = useNavigate();
  const { dispatch } = useContext(AuthContext);

  const {fetch, loading, response, error} = useAxios(LOGIN_AXIOS_CONFIG, false)

  useEffect(() => {
   if (response) // do something
   if (error) // do something else
  },[response,error])

  const handleLogin = (e) => {
    e.preventDefault();
    fetch()
  };

  return (
    <div className="login">
      <div className="logo">
        <h1>LOGO</h1>
        <h3>LOGO DESCRIPTION</h3>
      </div>
      <form onSubmit={handleLogin}>
        <input type="number" placeholder="phone" onChange={(e) => setPhone(e.target.value)} />
        <input type="password" placeholder="password" onChange={(e) => setPassword(e.target.value)} />
        <button type="submit">Submit</button>
        {loginError && <span>{loginError}</span>}
      </form>
    </div>
  );
};

export default Login;

use-axios.js

export const useAxios = (axiosParams, isAuto) => {
  const [response, setResponse] = useState(undefined);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(true);

  const fetchData = async (params) => {
    try {
      const result = await axios.request({
        ...params,
        method: params.method || 'GET',
        headers: {
          accept: 'application/json',
          authorization:
            'Bearer my-token',
        },
      });
      console.log(result.data);
      setResponse(result.data);
    } catch (error) {
      setError(error);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    if (isAuto)
    fetchData(axiosParams);
  }, [axiosParams, isAuto]); // execute once only

  return { fetch: () => fetchData(axiosParams), response, error, loading };
};
  • Related