Re-Usable fetch function with query string - reactjs

I have a fetch function inside of my react component, which I wish to "outsourse" in a separate component.
export const fetchBooksBySubject = (selectedValue) => {
const options = {
method: `GET`,
};
fetch(`${server}/books?subjects_like=${selectedValue}`, options)
.then((response) => {
if(response.ok){
return response.json()
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data: ', error)
})
}
Basically selectedValue is a prop coming from a child of App.jsx. As soon as the value is selected in a component, fetch should fire with this value in a query string. I tried to export the function above as a component and use it in App.jsx
useEffect(() => {
fetchBooksBySubject(selectedValue).then(data => setBookList(data));
}, [selectedValue])
const handleChange = e => {
setSelectedValue(e);
fetchBooksBySubject(selectedValue);
};
But this throws Property 'then' does not exist on type 'void'.

Here's a custom hook you can use with fast and reusable data fetching, a built-in cache, and other features like polling intervals and revalidation.
Hook:
const useBooks = (selectedValue) =>
{
const fetcher = (...args) => fetch(...args).then(res => res.json())
const { data, error } = useSWR(`/api/books?subjects_like=${selectedValue}`, fetcher)
return {
books: data,
isLoading: !error && !data,
isError: error
}
}
Usage:
const { books, isLoading, isError } = useBooks(selectedValue)
if (isLoading) return <div>Loading...</div>
else return <div>Your content here</div>
swr docs
Without swr:
useEffect(() =>
{
const fetchData = async (selectedValue) =>
{
const books = await fetchBookBySubject(selectedValue)
setBookList(books)
}
fetchData(selectedValue)
}, [selectedValue, bookList])

So the problem was, that I wasn't returning my fetch. I am a beginner, so my understanding is, that my App.js just couldn't access the data from fetchBooksBySubject withot this return
const dev = process.env.NODE_ENV !== 'production';
const server = dev ? 'http://localhost:3001' : 'https://your_deployment.server.com';
// later definable for developement, test, production
export const FetchBooksBySubject = (selectedValue) => {
const options = {
method: `GET`,
};
return fetch(`${server}/books?subjects_like=${selectedValue}`, options)
.then((response) => {
if(response.ok){
return response.json()
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data: ', error)
})
}
Same as here:
let sum = (a,b) => {a+b}
sum(1,2) //undefined
let sum1 = (a,b) => {return a+b}
sum1(1,2) //3

Related

TypeError: Cannot read properties of undefined (reading 'setRestaurants')

I'm working on a project where I am trying to fetch a list of restaurants from a database and display them on the screen.
When I run the code below I get this error:
TypeError: Cannot read properties of undefined (reading
'setRestaurants')
at CustomerDashPage.js:39
at async fetchList (CustomerDashPage.js:39)
at async getList (CustomerDashPage.js:32)
I know the fetch from the database works as I can console.log restaurants after I get them and all the tags from the database are the same as what is initially in the useState.
const [restaurants, setRestaurants] = useState([
{
Restaurant_id: "R763567026",
Restaurant_menuID: 0,
Restaurant_name: "Boston Pizza",
Restaurant_address: "271 Blackmarsh Rd",
Restaurant_postal: "P1Z 7A5",
Restaurant_username: "firstrest",
Restaurant_orders: ["O415052628", "O321764897", "O252073901", "O724516036"],
Restaurant_menuID: "M859068353",
Restaurant_category: "Japanese",
Restaurant_availability: true,
Restaurant_openHour: "11h00",
Restaurant_closeHour: "22h00",
},
]);
useEffect(() => {
const getList = async () => {
const fetchRest = await fetchList('R763567026');
}
getList();
}, [])
const fetchList = async (id) => {
try {
const resp = await fetch("/restaurant/id/" + id)
.then((resp) => resp.json())
.then(data => this.setRestaurants(data)).then(console.log(restaurants))
.catch(err => console.log(err));
}
catch (err) {
throw err;
console.log(err);
}
return true;
}
//Controls what happens when a restaurant is selected.
const selectRestaurant = async (id) => {
console.log(id);
};
return (
<div>
<Header />
<RestaurantList
itemList={restaurants}
component={RestaurantCard}
onSelect={selectRestaurant}
>
{" "}
</RestaurantList>
</div>
);
};
export default CustomerDash;
Any help would be much appreciated
As Abu mentioned in his answer, you need to call setRestaurants, not this.setRestaurants. Also, since you are using async/await syntax, you don't need all of those .then() calls.
const fetchList = async (id) => {
const response = await fetch(`/restaurant/id/${id}`).catch((err) => throw err);
const json = await response.json();
setRestaurants(json);
console.log(restaurants);
return true;
};
It's functional component so use setRestaurants instead of this.setRestaurants
const fetchList = async (id) => {
try {
const resp = await fetch("/restaurant/id/" + id)
.then((resp) => resp.json())
.then(data =>
setRestaurants(data))
.catch(err => console.log(err));
}
catch (err) {
throw err;
console.log(err);
}
}
After updating state, you won't get state value instantly. so your console.log(restaurants) won't work.

Next.js using SWR with axios

I'm trying to use SWR to prefetch data in my project.
Here is my code:
export const getStaticProps = async (res) => {
const result = await axios.get(
`/orders/detail/${res.params.cid}/${res.params.oid}`
);
const orderDetailById = await result.data;
return {
props: { orderDetailById },
};
};
export const getStaticPaths = async () => {
const result = await fetch(`${server}/api/orders`);
const orders = await result.json();
const ids = orders.map((order_detail) => ({
oid: order_detail.oid,
cid: order_detail.cid,
}));
const paths = ids.map((id) => ({
params: { oid: id.oid.toString(), cid: id.cid.toString() },
}));
return {
paths,
fallback: false,
};
};
const fetcher = (url, params) => {
return fetch(url + params.cid + '/' + params.oid).then((r) => r.json());
};
const OrderDetailByOId = ({ orderDetailById }) => {
const cid = orderDetailById.customer[0].cid;
const oid = orderDetailById.detail[0].oid;
const params = useMemo(() => ({ cid, oid }), [cid, oid]);
const { data, error } = useSWR(['/orders/detail/', params], fetcher, {
initialData: orderDetailById,
});
if (error) {
console.log('errorHere', error);
return <div>failed to load</div>;
}
if (!data) return <div>Loading...</div>;
return <OrderDetailForm orderDetailById={orderDetailById} />;
};
export default OrderDetailByOId;
It works well in the first render.
At the same time, I didn't change any data in my database,
so when it renders the second time by refreshInterval:1000 it wouldn't change anything, but it popped up with some errors!
errorHere SyntaxError: Unexpected token < in JSON at position 0
When I first saw the error I guessed it was just some JSON problems, so I changed the fetcher's return like (r)=>r.data
After I changed this, it caused the web to return loading...
It means it didn't fetch anything in the second render or even each after the first render.
Did anyone can help me find out what problems caused the errors.
Thanks~
I forgot I have set Axios basic URl like Axios.defaults.baseURL = server + '/api';
so I changed the fetcher return like return axios.get(url + params.cid + '/' + params.oid).then((r) => r.data);
It works for me now~ Thanks for the #juliomalves pointing me out where could be a problem ~ Thanks!

React Native - state is returning null after setting state

I'm very much new to react native currently i'm building small app for just getting an idea about this. I'm facing an issue in mapping the data from API. This is the json response returning from the api
{"data":[{"digit":300,"countsum":"52"},{"digit":301,"countsum":"102"},{"digit":302,"countsum":"27"},{"digit":303,"countsum":"201"},{"digit":500,"countsum":"101"}]}
When i tried to map this data i'm facing some issues. I stored the response from API to the state and when i tried to display the state data using map function it's showing the state value is null. This the code i tried till now
const [listdata, setListData] = useState(null)
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
listdata.map(item => <Text>{item.digit}</Text>)
}
Do it like this,
export default function ComponentName () {
const [listdata, setListData] = useState([])
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
}
return (<>
listdata.map(item => <Text>{item.digit}</Text>)
</>
);
}
You have to wait the fetch execution and later do the list map.
// wait for it
await axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
listdata.map(item => <Text>{item.digit}</Text>)
If you want to map the data then do that inside return statement of your code ,like:
return(
{listData?listdata.map(item => return <Text>{item.digit}</Text>):""}
);
This is a sample of a meant in my comment above:
Try console.log listdata at this stage, you will find that it is still
null, in other words, the value of the updated value of the
listdata:useSate will be ready after the render take place. You can
make another function outside of the current one. then use useEffect
with listdata to update your text views
const [listdata, setListData] = useState(null)
useEffect(() => makeRemoteRequest(), [listdata])
makeRemoteRequest = () => {
const url = `your-url-of-data-here`;
fetch(url)
.then(res => res.json())
.then(res => {
setListData(res.data);
})
.catch(error => {
console.log(error)
});
};
You could try the following:
const [listdata, setListData] = useState([])
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
try {
const dataResponse = await axios.get(constants.BASE_URL + "getlist?token=" +token);
setListData(dataResponse.data || [] );
} catch(error) {
console.log(error);
}
}
return (<>
listdata.map(item => <Text>{item.digit}</Text>)
</>);

React - fetch data and store in a variable

Beginner with react and struggling with something that I am sure is probably very simple. I am just trying to make a simple component that will fetch data and display a part of it in a div. I am able to get the data and print it to console, but I am having trouble saving to a variable and displaying it. Here is my code (removed the actual url for privacy reasons):
let x = -1;
function getData(apiUrl){
fetch(apiUrl, {credentials: 'same-origin'})
.then((response) => {
if (!response.ok) {
Logging.error(`Did not get an ok. got: ${response.statusText}`);
}
return response.json();
})
.then(json => {x = json.value})
.catch((error) => {
Logging.error(`Error getting ad data: ${error.message}`);
})
}
const MyPage = () => {
getData('my endpoint')
return (
<div>{x}</div>
);
}
My issue is when I load the page it always displays my default value of "-1". So either x is never getting re-assigned, or the return is happening before it does.
Other commenters about setting state is not wrong.
However, you are also not exactly wrong, expecting a value for x.
Your getData function calls fetch, however you did not return anything from fetch. If you want to use x = getData(), you will need to ensure to add a return before the fetch function in order to return the data.
const getData = (apiUrl) => {
return fetch(apiUrl, {credentials: 'same-origin'})
.then((response) => {
if (!response.ok) {
Logging.error(`Did not get an ok. got: ${response.statusText}`);
}
return response.json();
})
.then(json => {x = json.value})
.catch((error) => {
Logging.error(`Error getting ad data: ${error.message}`);
})
}
let x = await getData(apiUrl)
However, fetch is asynchronous so it's you need to use x = await getData().
You cannot use await outside an async function, so you need to use effect, and useState to properly render the data you want.
import React, { useEffect, useState } from 'react';
const MyPage = () => {
const [ data, setData ] = useState();
useEffect(() => {
getData(apiUrl);
},[])
const getData = async (apiUrl) => {
fetch(apiUrl, {credentials: 'same-origin'})
.then((response) => {
if (!response.ok) {
Logging.error(`Did not get an ok. got: ${response.statusText}`);
}
return response.json();
})
.then(json => setData(json)) //setData here
.catch((error) => {
Logging.error(`Error getting ad data: ${error.message}`);
})
}
return (<pre>{ JSON.stringify(data, null, 4)}</pre>)
}
You need to use JSON.stringify to show your JSON results in your return statement.
You need to you use the state in react. Try something like:
import react, { useState, useEfect } from 'react';
const MyPage = () => {
const [data, setData] = useState(null);
const useEfect(() => {
const result = getData('my endpoint');
setData(result);
}, []);
return (
<div>{data}</div>
);
}

Map through Data in React and recieving - TypeError: Object(...)(...) is undefined

I'm receiving the error TypeError: Object(...)(...) is undefined. When trying to map.
drinks
0
strDrink "'57 Chevy with a White License Plate"
strDrinkThumb "https://www.thecocktaildb.com/images/media/drink/qyyvtu1468878544.jpg"
idDrink "14029"
1
strDrink "155 Belmont"
strDrinkThumb "https://www.thecocktaildb.com/images/media/drink/yqvvqs1475667388.jpg"
idDrink "15346"
2
strDrink "747 Drink"
strDrinkThumb "https://www.thecocktaildb.com/images/media/drink/8ozumt1572901761.jpg"
idDrink "178318"
Drinks is an Array of Objects.
Im fetching the data in context
const [cocktails, setCocktails] = useState([]);
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/';
const fetchCocktailList = async () => {
try {
const res = await fetch(`${baseUrl}filter.php?c=Cocktail`);
const data = await res.json();
console.log(data);
return data;
} catch (err) {
console.log('Error fetching data');
}
};
useEffect(() => {
setLoading(true);
fetchCocktailList().then((data) => setCocktails(data));
setLoading(false);
}, []);
and maping like so
const DrinkList = ({ drinks, isLoading }) => {
return (
<div className='drink-list-wrapper'>
{isLoading ? (
<h2>Loading...</h2>
) : (
drinks.map((drink) => <DrinkItem key={drink.idDrink} drink={drink} />)
)}
</div>
If someone could explain this to me? I've tried making the initial value of cocktails to an empty object first.
const DrinkPage = () => {
const { cocktails } = useContext(DataContext);
const { isLoading } = useContext(DataContext);
return (
<div>
<DrinkList drinks={cocktails} isLoading={isLoading} />
</div>
);
};
I have console.logged data fetched and it says its an object.
fetchCocktailList is returning the data, however the hook is interpreting that it returns a Promise: fetchCocktailList().then((data) => setCocktails(data));
My suggestion is to move fetchCocktailList inside the hook as it is only being used there; move the logic related to the fetch (state changes) inside the function.
const [cocktails, setCocktails] = useState([]);
useEffect(() => {
const fetchCocktailList = async () => {
const baseUrl = "https://www.thecocktaildb.com/api/json/v1/1/";
setLoading(true);
try {
const res = await fetch(`${baseUrl}filter.php?c=Cocktail`);
const data = await res.json();
console.log(data);
setCocktails(data);
setLoading(false);
} catch (err) {
console.log("Error fetching data");
setLoading(false);
}
};
fetchCocktailList();
}, []);
This however doesn't seem related to the error you get, but it might help to figure it out.
The error might come from the returned data, make sure it is returning the array of items and not an object with a property containing the array, for example: { drinks: [] }.

Resources