How to process data received from an AJAX request in React - reactjs

I have a custom hook named "useFetch" which makes an AJAX request and stores the result in the state. I simply want to format the data received from the ajax using a function in my component but not sure how to do this since the function needs to be called only after the data is received.
An example is below:
import React, { Component, useState } from "react";
import useFetch from "../../../Hooks/useFetch";
const Main = () => {
const { data, isPending, error } = useFetch(
"http://127.0.0.1:8000/api/historic/1"
);
function formatData(data){
//Do some processing of the data after it's been received
}
//This doesn't work of course because it runs before the data has been received
const formatted_data=formatData(data);
return (
//Some display using the formatted data
);
};
export default Main;
This is the custom hook, useFetch, which is used in the above component. I'd prefer to not have to do the formatting in here because the formatting is specifically related to the above component and this custom hook is designed to have more universal utility.
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null);
const [isPending, setisPending] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortCont = new AbortController();
fetch(url, { signal: abortCont.signal })
.then((res) => {
if (res.ok) {
return res.json();
} else {
throw Error("could not fetch data for that resource");
}
})
.then((data) => {
setData(data);
setisPending(false);
setError(null);
})
.catch((er) => {
if (er.name === "AbortError") {
console.log("fetch aborted");
} else {
setError(er.message);
setisPending(false);
}
});
return () => abortCont.abort();
}, [url]);
return { data, isPending, error };
};
export default useFetch;

You should wrap it with useEffect hook with data as it's deps.
const [formattedData, setFormattedData] = useState();
useEffect(() => {
if (!data) return;
const _formattedData = formatData(data);
setFormattedData(_formattedData);
}, [data]);

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]);

react native pagination with search using flatlist and hook

react native search using search key send as parameter for Api call which was text input in search filled and along with it react native pagination using Flatlist and hook , problem faced that i have added data when next page loaded, but during seach key enter it stored its previous value
When you type on input search, you first should reset data state: setData([]).
setData is async and api request also, then, it is possible than reset failed some times.
For this reason, I use flags with useRef, to write and read value synchronous way, example:
const resetData = useRef(false)
when type on filter:
resetData.current=true
and on .then api req:
if(resetData.current){
setData(response.data);
resetData.current = false
}
else{
setData([...data,response.data])
}
Edit(after your comments):
import React, { useState, useEffect, useRef } from "react";
//..other imports
function App() {
const [data, setData] = useState([]);
const [page, setPage] = useState(1);
const [searchKey, setSearchKey] = useState();
const resetData = useRef(false);
const getData = () => {
let pageToReq = page;
if (resetData.current) {
pageToReq = 0;
}
const headers = { Authorization: "token" };
axios
.get("baseurl" + "getdata?page=" + pageToReq, { searchingKey: searchKey })
.then(async function (response) {
if (resetData.current) {
setData(response.data);
resetData.current = false;
} else {
setData([...data, ...response.data]);
}
})
.catch(function (error) {
console.log(error);
});
};
useEffect(() => {
getData();
}, [page, searchKey]);
const handleOnChangeText = (val) => {
resetData.current = true;
setSearchKey(val);
};
const handleOnEnd = () => {
if (resetData.current) {
return;
}
setPage(page + 1);
};
return (
<View>
<TextInput onChangeText={handleOnChangeText} />
<FlatList
data={data}
onEnd={handleOnEnd}
onEndReachThreshold={0.1}
></FlatList>
</View>
);
}
export default App;

UseFetch returns Can't perform a React state update on an unmounted component

When I'm using this useFetch hook my application shows a blank screen and returns:
Can't perform a React state update on an unmounted component. This is
a no-op, but it indicates a memory leak in your application. To fix,
cancel all subscriptions and asynchronous tasks in a useEffect cleanup
function.
I don't understand why it goes wrong.
import { useState, useEffect } from "react";
export default function useFetch(url) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
(async function () {
try {
setLoading(true);
const response = await fetch(url, {
method: "GET"
});
const data = await response.json();
setData(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
})();
}, [url]);
return { data, error, loading };
}
export const useFetchCarById = (testId) =>
useFetch(
`https://localhost/cars/${testId}`
);
export default function Foo() {
const { cars, car } = useContext(Context);
const { data, error, loading } = useFetchCarById(car);
return (
<div>
{data && data.map((x) => <p>{x.startTime}</p>)}
</div>
);
}

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

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