React custom hooks showing undefined error in Axios - reactjs

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?

Related

React Fetching Request returns null after refresh

So I'm working on a school project right now and I've created a backend using express and nodejs. I want to retrieve data and only get the questions that are associated with the current category. After retrieving the data it gives the data I want but then when I refresh the page it only gets null. What am I doing wrong?
Fetch Hook
import axios from 'axios';
export default function useFetch(name) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
(async function () {
try {
setLoading(true);
const response = await axios
.get('http://localhost:3001/api/getQuestions')
.then((res) => {
const dataArray = res.data;
const questionArray = dataArray.filter((question) => {
return question.questionCategory === 'installation';
});
setData(questionArray);
});
console.log(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
})();
}, [name]);
return { data, error, loading };
}
Quiz Component
import fetchQuestion from '../../../hooks/fetchQuestion';
const InstallationsQuiz = () => {
const { data, loading, error } = fetchQuestion('installation');
useEffect(() => {
data.map((item) => {
console.log(item);
});
}, [data]);

Call custom hook twice in the same component

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)

Using and writing React Hooks

I have this component:
import React, { Component } from 'react';
import useFetch from "../useFetch";
export class Patient extends Component {
static displayName = Patient.name;
constructor(props) {
super(props);
}
componentDidMount() {
alert("fgggg");
const { isLoading, serverError, apiData } = useFetch(
"/Patient/GetPatients"
);
}
render() {
return (
<div>
</div>
);
}
}
I want to call the useFetch, here is my useFetch:
import React, { useEffect, useState } from "react";
function useFetch(url){
const [isLoading, setIsLoading] = useState(false);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
alert("dddd");
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
try {
fetch(url)
.then(response => response.json())
.then(data => setApiData(data));
//const resp = await axios.get(url);
//const data = await resp?.data;
setIsLoading(false);
} catch (error) {
alert(error);
setServerError(error);
setIsLoading(false);
}
};
fetchData();
}, [url]);
return { isLoading, apiData, serverError };
};
export default useFetch;
Erro:
Attempted import error: 'useFetch' is not exported from '../useFetch'.
Can anybody advise?
UPDATE
Thanks for the resource in the answer, but i found this https://blog.bitsrc.io/fetching-data-in-react-using-hooks-c6fdd71cb24a
and now i have changed my code to this:
import React, { useEffect, useState } from "react";
export default function useFetch(url, opts){
const [response, setResponse] = useState(null)
const [loading, setLoading] = useState(false)
const [hasError, setHasError] = useState(false)
useEffect(() => {
setLoading(true)
fetch(url, opts)
.then((res) => {
setResponse(res.data)
setLoading(false)
})
.catch(() => {
setHasError(true)
setLoading(false)
})
}, [url])
return [response, loading, hasError]
}
and
import React, { Component } from 'react';
import useFetch from "../useFetch";
export class Patient extends Component {
static displayName = Patient.name;
constructor(props) {
super(props);
}
componentDidMount() {
alert("fgggg");
const [ response, loading, hasError ] = useFetch("", "");
}
render() {
return (
<div>
</div>
);
}
}
I still get this error
×
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
Instead of exporting at the end of the file you could export when defining the function/hook.
export default function useFetch(url) {
const [isLoading, setIsLoading] = useState(false);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
alert("dddd");
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
try {
fetch(url)
.then((response) => response.json())
.then((data) => setApiData(data));
//const resp = await axios.get(url);
//const data = await resp?.data;
setIsLoading(false);
} catch (error) {
alert(error);
setServerError(error);
setIsLoading(false);
}
};
fetchData();
}, [url]);
return { isLoading, apiData, serverError };
}
As well double check your import path is correct.
Good reference for when creating custom hooks and using them: https://www.freecodecamp.org/news/how-to-create-react-hooks/
UPDATED: As Hozeis commented. You cannot use hook inside class components. Just noticed you were using a class component

How to test component that uses custom hook with React-testing-library?

I have a custom hook to make async calls with setting errors, loadings etc.
import { useEffect, useState } from 'react';
const useMakeAsyncCall = ({ asyncFunctionToRun = null, runOnMount = false }) => {
const [response, setResponse] = useState(null);
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const fetchData = async () => {
setLoading(true);
try {
const res = await asyncFunctionToRun();
const json = await res.json();
setResponse(json);
setLoading(false);
} catch (error) {
setError(error);
setLoading(false);
}
};
useEffect(() => {
if (runOnMount && asyncFunctionToRun !== null) fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [runOnMount]);
return { response, error, loading, fetchData };
};
export default useMakeAsyncCall;
In component I am using it like this
const { error, isLoading, fetchData } = useMakeAsyncCall({
asyncFunctionToRun: () => signUpUser(),
runOnMount: false,
});
const signUpUser = () => {
...some requests to firebase
};
const handleSumbit = (e) => {
e.preventDefault();
fetchData();
};
Now I am trying to test this logic.
it('does things', async () => {
const { container, getByTestId } = render(<Component/>);
const form = getByTestId('form');
fireEvent.submit(form);
expect(container.firstChild).toMatchSnapshot();
});
And I'm getting this error Warning: An update to Component inside a test was not wrapped in act(...) and it is pointing to setError and setLoading inside my hook. How to go about fixing it and testing this functionality?

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