Call custom hook twice in the same component - reactjs

I want to use this custom hook for making api requests:
export default function useFetch({ method, url, data = null, config = null }) {
const [response, setResponse] = useState(null);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
api[method](url, JSON.parse(config), JSON.parse(data))
.then((res) => {
setResponse(res.data);
})
.finally(() => {
setIsLoading(false);
});
} catch (err) {
setError(err);
}
};
fetchData();
}, [api, method, url, data, config]);
return { response, error, isLoading };
}
The above code is not important. So do not pay much attention to it. My question is how I can make two api calls within the same component. Is that possible?
export const programApi = axios.create({
baseURL: programApiUrl,
});
const {response, isLoading} = useFetch({
api: programApi,
method: "get",
url: "/SportsProgram/active_sport_type",
config: JSON.stringify({ requireAuthentication: true }),
});
useEffect(() => {
if (response !== null) {
// do more stuff if you wish
}
}, [response]);
Is it possible to use useFetch twice?

You can rename the values in the object when destructing them in your component like so:
const {response, isLoading} = useFetch({
api: programApi,
method: "get",
url: "/SportsProgram/active_sport_type",
config: JSON.stringify({ requireAuthentication: true }),
});
const {response: response2, isLoading: isLoading2} = useFetch({
api: programApi,
method: "get",
url: "/SportsProgram/active_sport_type",
config: JSON.stringify({ requireAuthentication: true }),
});
console.log(response, response2)
Or instead of returning an object in your hook return an array. Then in your component you can destruct them and call them different names.
export default function useFetch({ method, url, data = null, config = null }) {
const [response, setResponse] = useState(null);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
api[method](url, JSON.parse(config), JSON.parse(data))
.then((res) => {
setResponse(res.data);
})
.finally(() => {
setIsLoading(false);
});
} catch (err) {
setError(err);
}
};
fetchData();
}, [api, method, url, data, config]);
return [ response, error, isLoading ];
}
Then in your component you can do like :
const [firstResponse, firstError, firstIsLoading] = useFetch(...your stuff)
const [secondResponse, secondError, secondIsLoading] = useFetch(...your stuff)

Related

React custom hooks showing undefined error in Axios

I have a cutom hook created as below. It is using axios and useEffect().
import { useState, useEffect } from 'react';
import axios from 'axios';
axios.defaults.baseURL = 'https://jsonplaceholder.typicode.com';
const baseURL="http://127.0.0.1:5000/v1/test_server/"
const useAxios = ({ url, method='get', body = null, headers = JSON.stringify({ accept: '*/*' }),
}) => {
const [response, setResponse] = useState(null);
const [error, setError] = useState('');
const [loading, setloading] = useState(true);
const fetchData = () => {
axios[method](baseURL+url, JSON.parse(headers), JSON.parse(body))
.then((res) => {
setResponse(res.data);
})
.catch((err) => {
setError(err);
})
.finally(() => {
setloading(false);
});
};
useEffect(() => {
fetchData();
}, [method, url, body, headers]);
return { response, error, loading };
};
export default useAxios;
I'm using this hook in another component as
const [playerData, setPlayerData] = useState();
const { playerResponse, playerError, playerLoading} = useAxios({ url: 'player/get_player'})
if (playerResponse !== null ) {
setPlayerData(playerResponse.result);
console.log("Response", playerData);
}
Everything seems correct but the code is showing Cannot read properties of undefined (reading 'result'). Why this is happening?

How to get router.push to work correctly in this example?

For the following code, after the server returns 200, router.push doesn't actually do anything
import { useRouter } from 'next/router'
export default function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errHidden, setErrHidden] = useState(true)
const router = useRouter()
useEffect(() => {
router.prefetch("/editor")
}, [])
const handleEmailChange = (e) => {
setEmail(e.target.value);
};
const handlePasswordChange = (e) => {
setPassword(e.target.value);
};
const handleSubmit = async (event) => {
event.preventDefault();
const response = await fetch(`/api/login`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ email, password }),
});
if (response.status === 200) {
console.log('pushing editor');
router.push("/editor");
console.log(router)
} else {
setErrHidden(false)
}
};
return (<Component>...</Component>)
It does log the first console.log statement correctly. The second one prints out the router object but has no evidence that the push function was called. Not seeing any errors or warnings in the console or terminal either.

ReactJS - passing data between components

I want to passing data between components but I have a problem. Not getting any error as long as I don't passing data yet, it's fine. When I try to show the props in the console, I can easily see what I want (history,match,location,AuthStore). But when I try to pass the data, I can only see value and AuthStore in the console and value returns empty. Where am I wrong?
front.layout.js
import Profile from '../../Views/profile'
const Layout = (props) => {
const [user, setUser] = useState({});
const [isLoggedIn, setIsLoggedIn] = useState(false);
props.AuthStore.getToken();
const history = useHistory();
useEffect(() => {
const token =
props.AuthStore.appState != null
? props.AuthStore.appState.user.access_token
: null;
axios
.post(
"/api/authenticate",
{},
{
headers: {
Authorization: "Bearer " + token,
},
}
)
.then((res) => {
if (!res.data.isLoggedIn) {
history.push("/login");
}
setUser(res.data.user);
setIsLoggedIn(res.data.isLoggedIn);
})
.catch((error) => {
history.push("/login");
});
}, []);
return (
<>
<Profile value={user} />
</>
)
index.js
const Profile = (props) => {
console.log(props);
const { params } = props.match;
const [data, setData] = useState({});
const history = useHistory();
if(props.location.key){
useEffect(() => {
axios
.get(
`/api/${params.username}`,
{
headers: {
Authorization:
"Bearer " +
props.AuthStore.appState.user.access_token,
},
}
)
.then((res) => {
if (res.data.username) {
setData(res.data);
}
})
.catch((error) => {
console.log(error);
});
}, []);
}

Custom hook and dynamic useEffect dependency

I'm using this useFetch custom hook in multiple components.
import {useState,useEffect} from 'react'
import {getToken} from './../services/token'
const useFetch = (url) => {
const [data,setData] = useState(null);
const [error,setError] = useState(null);
const [loading,setPending] = useState(false);
useEffect(() => {
(async()=>{
const abortCont = new AbortController();
const token = await getToken();
try {
setPending(true);
const response = await fetch(url,{
signal : abortCont.signal,
headers : {
"Content-Type" : "application/json",
"Authorization" : "Bearer "+token
}
});
const result = await response.json();
setData(result);
setPending(false);
} catch (err) {
if(err.name === 'AbortError'){
abortCont.abort();
return;
}
setError(String(err));
setPending(false);
}
})()
}, [])
return {data,error,loading}
}
export default useFetch;
And I import it in a users components as below.
const {data,error,loading} = useFetch('http://localhost:4040/users');
Normally I pass the second parameter of UseEffect like [users.length] to make the users data reactive but as this is a custom hook and being used in many components,
How can I make the users data to be reactive when users data was changed or updated?
Codesandbox Link
You can pass the dependency array as the optional parameter to your custom hook.
const useFetch = async (url, deps) => {
const [data,setData] = useState(null);
const [error,setError] = useState(null);
const [loading,setPending] = useState(false);
useEffect(() => {
(async()=>{
const abortCont = new AbortController();
const token = await getToken();
try {
setPending(true);
const response = await fetch(url,{
signal : abortCont.signal,
headers : {
"Content-Type" : "application/json",
"Authorization" : "Bearer "+token
}
});
const result = await response.json();
setData(result);
setPending(false);
} catch (err) {
if(err.name === 'AbortError'){
abortCont.abort();
return;
}
setError(String(err));
setPending(false);
}
})()
}, deps || [])
return {data,error,loading}
}
export default useFetch;
Then call it like
const {data,error,loading} = useFetch('http://localhost:4040/users', [users.length]);
EDIT
In your case you want to have some more control over the rendering. So i'll say you have to modify your hook to support setting data for your mounted component.
const useFetch = async (url, holded, setHolded) => {
const [data,setData] = useState(null);
const [error,setError] = useState(null);
const [loading,setPending] = useState(false);
useEffect(() => {
(async()=>{
const abortCont = new AbortController();
const token = await getToken();
try {
setPending(true);
const response = await fetch(url,{
signal : abortCont.signal,
headers : {
"Content-Type" : "application/json",
"Authorization" : "Bearer "+token
}
});
const result = await response.json();
setData(result);
if(setHolded) {
setHolded(result);
}
setPending(false);
} catch (err) {
if(err.name === 'AbortError'){
abortCont.abort();
return;
}
setError(String(err));
setPending(false);
}
})()
}, (holded && setHolded && [holded,setHolded,url]) || [url])
return {data,error,loading}
}
export default useFetch;
Then call it like
const {data,error,loading} = useFetch('http://localhost:4040/users', users, setUsers);

Access function argument inside function react hooks

I am writing a custom react hook for fetching data from an endpoint. This is what the function looks like
import { useState } from "react";
const useHttp = async (endpoint, method, data) => {
const [loading, setLoading] = useState(false)
const [fetchedData, setfetchedData] = useState(null)
setfetchedData(await fetch.method(endpoint));
return [isLoading, fetchedData]
}
export default useHttp;
As you can see, I want to do a fetch request to whatever method is passed on to the useHttp hook. Please someone point me how to do it?
You cannot pass async functions to React Hooks. You have to useEffect
import { useState, useEffect } from "react";
const useHttp = (endpoint, method, options) => {
const [isLoading, setLoading] = useState(false);
const [fetchedData, setFetchedData] = useState(null);
useEffect(() => {
setLoading(true);
fetch(endpoint, { method, ...options })
.then(data => data.json())
.then((json) => {
// do something with JSON data
setFetchedData(json);
})
.catch((err) => {
// do something with err
})
.finally(() => {
setLoading(false);
});
}, []);
return [isLoading, fetchedData];
};
export default useHttp;
Use useEffect hook to make the HTTP request.
fetch function takes an optional second argument which is an object specifying various options for the HTTP request and one of the options is a method option. Use this method option to specify the request method.
import { useState, useEffect } from "react";
const useHttp = async (endpoint, method, data) => {
const [loading, setLoading] = useState(false);
const [fetchedData, setfetchedData] = useState(null);
useEffect(() => {
setLoading(true);
fetch(endpoint, { method })
.then(res => res.json())
.then(data => {
setLoading(false);
setfetchedData(data);
})
.catch(err => {
setLoading(false);
console.log(err.message);
});
}, []);
return [isLoading, fetchedData];
}
For details on how to specify options for fetch function and different options that can be specified, see using fetch
If you want to use async-await syntax, you can write useEffect hook as:
useEffect(() => {
async function makeRequest() {
setLoading(true);
try {
const response = await fetch(endpoint, { method });
const data = await res.json();
setLoading(false);
setfetchedData(data);
} catch (error) {
setLoading(false);
console.log(err.message);
}
}
makeRequest();
}, []);
hi maybe this help you:
1- call function:
const useHttp = async (url,method,data)=>{
var options = {
method:method,
headers: {
'Content-Type': 'application/json; charset=utf-8;'
}
};
if(method==='POST' && data)
options.body = JSON.stringify(data);
const response = await fetch(url, options);
const rep = await response.json();
console.log(rep);
return rep;
};
in above code first create your request options and then send it by fetch to end point.
2- use it in compoent like below:
setLoading(true);
var rep = await useHttp(...)
setLoading(false);

Resources