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 };
};