Using react useEffect hook - reactjs

I'm using React useEffect hook for getting data and display the loading indicator but my loading is not working.
Heres the useEffect Hook code:
useEffect(() => {
fetchEvents();
}, []);
fetchEvents function code:
const fetchEvents = () => {
setLoading(true);
const requestBody = {
query: `
query {
events {
_id
title
description
price
date
creator {
_id
email
}
}
}
`
};
fetch("http://localhost:5000/graphql", {
headers: {
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify(requestBody)
})
.then(res => {
if (res.status !== 200 && res.status !== 201) {
throw new Error("Failed");
}
return res.json();
})
.then(resData => {
const events = resData.data.events;
setEvents(events);
setLoading(false);
})
.catch(err => {
console.log(err);
setLoading(false);
});
};

You should give more info but here an example for you:
import React, { useState, useEffect } from 'react';
import { Spinner } from 'react-bootstrap';
const MyComponent = () => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
useEffect(() => {
setIsLoading(true);
fetch('/data/endpoint')
.then((res) => res.json)
.then((response) => {
setData([...response]);
setIsLoading(false);
});
}, []);
return isLoading ? (
<Spinner />
) : (
<ol>
data.map(items => <li>{items.label}</li>);
</ol>
);
};

The first parameter of useEffect (the function) is only called if one object of the second parameter (the list) is modified. If the list is empty, it never happens.
You could remove the second parameter to apply the function fetchEvents at each update or you could use any constant to run fetchEvents only once.

Related

Issue with fetching data

iam new to React and trying to show data from API,
It works at first but after reload i got error " Cannot read properties of undefined (reading 'length')",
any ideas what could it cause ?
thanks
code looks like this:
import React from "react";
import { useEffect, useState } from "react";
const options = {
//options for API call
};
const Ticket = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch(
"https://daily-betting-tips.p.rapidapi.com/daily-betting-tip-api/items/daily_betting_coupons?sort=-id",
options
)
.then((res) => res.json())
.then((data) => {
setData(data);
})
.catch((err) => {
console.log(err);
})
.finally(() => {
setLoading(false);
});
}, []);
if (loading) {
return <p>data is loading...</p>;
}
return (
<div>
<h1>length: {data.data.length}</h1>
<h2></h2>
</div>
);
};
export default Ticket;
You are getting this error because you have data state which is an array but in return you are trying to access data key from the state's data array, which is not there hence it returns the undefined and then you are trying to access the length from undefined.
Instead of data.data.length just use data.length
Use this code. I edited your code. Add a condition when set your data variable
if(data.data) {
setData(data.data)
}
And also change this line
<h1>length: {data.data.length}</h1>
To
<h1>length: {data.length}</h1>
Here is the full code
import React from "react";
import { useEffect, useState } from "react";
const options = {
//options for API call
};
const Ticket = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch(
"https://daily-betting-tips.p.rapidapi.com/daily-betting-tip-api/items/daily_betting_coupons?sort=-id",
options
)
.then((res) => res.json())
.then((data) => {
if (data.data) {
setData(data.data);
}
})
.catch((err) => {
console.log(err);
})
.finally(() => {
setLoading(false);
});
}, []);
if (loading) {
return <p>data is loading...</p>;
}
return (
<div>
<h1>length: {data.length}</h1>
<h2>Hello world</h2>
</div>
);
};
export default Ticket;

React Hook useEffect has a missing dependency: 'data'. Either include it or remove the dependency array

I have a filter function which is filtering data with state data, dataCopy and searchValue. Issue is if i don't include the data state than react gives warning and if i do include it it cause infinite loop cause the data array is changing within the useEffect. How can i make so that i don't get that warning.
Filter function
import React, { useEffect, useState } from 'react'
import Header from '../Components/Header/Header'
import Home from '../Components/Home/Home'
import "./Layout.css"
import Spinner from '../Components/Spinner/Spinner'
function Layout() {
// state for data, copy of data and spinner
const [data, setData] = useState([])
const [dataCopy, setDataCopy] = useState([])
// state for search input in Header.js (define in parent )
const [searchValue, setSearchValue] = useState("")
// changing search value
const changeSearchValue = (value) => {
setSearchValue(value)
}
// useEffect for search functionality
useEffect(() => {
const handleSearch = () => {
if (searchValue !== "") {
const searchFilter = data.filter(item =>
!isNaN(searchValue) ? item.expected_annually_bill_amount.toString().includes(searchValue) :
item.dmo_content.Ausgrid.toString().toLowerCase().includes(searchValue.toLowerCase()))
setData(searchFilter)
} else {
setData(dataCopy)
}
}
handleSearch()
}, [searchValue, dataCopy])
// useEffect for getting data from api
useEffect(() => {
// making post request to get the token
axios.post(`${process.env.REACT_APP_BASE_URL}`, { data: "" },
{
headers:
{
'Api-key': `${process.env.REACT_APP_API_KEY}`,
},
})
// after getting to token returning it for callback
.then((response) => {
return response.data.data.token
})
// using the token to call another api for the needed data
.then((tokenIs) => {
axios.post(`${process.env.REACT_APP_DATA_URL}`,
{ "session_id": `${process.env.REACT_APP_SESSION_ID}` },
{
headers:
{
'Api-key': `${process.env.REACT_APP_API_KEY}`,
'Auth-token': tokenIs,
},
})
.then((response) => {
setData(response.data.data.electricity)
setDataCopy(response.data.data.electricity)
setSpinner(false)
})
})
// catiching any error if happens
.catch((err) => {
setSpinner(false)
alert(err)
})
}, [])
return (<>
<div className='layout'>
<Header
changeSearchValue={changeSearchValue}
searchValue={searchValue}
/>
<Home data={data} />
</div>
)
}
export default Layout
Here you can eliminate data dependency by:
useEffect(() => {
const handleSearch = () => {
if (searchValue !== "") {
setData(data => data.filter(item =>
!isNaN(searchValue) ? item.expected_annually_bill_amount.toString().includes(searchValue) :
item.dmo_content.Ausgrid.toString().toLowerCase().includes(searchValue.toLowerCase())))
} else {
setData(dataCopy)
}
}
handleSearch()
}, [searchValue, dataCopy])
You can add the following comment above the dependency array for suppressing the warning
// eslint-disable-next-line react-hooks/exhaustive-deps

Call custom hook inside function

i need to call my custom hook called useAxios() inside function. As i know its not available in react, but i hope there's some method that do the trick
useAxios hook:
import { useState, useEffect } from "react";
import Axios from "axios";
const useAxios = (method: string, param: string, data: any) => {
const [response, setResponse] = useState([]);
const [error, setError] = useState([]);
const fetchData = () => {
Axios({
method: method,
url: `https://gernagroup-server.herokuapp.com/${param}`,
data: data,
})
.then((response) => {
setResponse(response.data);
})
.catch((err) => {
setError(err);
});
};
useEffect(() => {
fetchData();
}, []);
return { response, error };
};
export default useAxios;
I have to call this function, but using my hook
const handleNewSale = () => {
Axios.post("https://gernagroup-server.herokuapp.com/new-sale", {
data: selectedValues,
})
.then((response) => {
console.log(response);
showModal(ResultType.success, "New sale added successfully!");
})
.catch((err) => {
console.log(err);
showModal(ResultType.error, "Something went wrong");
});
};
I have to call this function on click
<Button text="New sale" onClick={handleNewSale} />
fetchData is async, you’re missing async and await keywords in code. Same for handleNewSale.
Avoid any types.
Give your custom hook a strict return type. Also you may need an export keyword in front of the ‘const’

how to change state by setState in React hooks

App.js
<Route path="/detail/:id" >
<PostDetail />
</Route>
PostDetail.js
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios'
const PostDetail = () => {
const { id } = useParams();
let [comments, setComments] = useState([]);
useEffect(async () => {
await axios
.all([
axios.get(`https://dummyapi.io/data/v1/post/${id}`, {
headers: { "app-id": process.env.REACT_APP_API_KEY }
}),
axios.get(`https://dummyapi.io/data/v1/post/${id}/comment`, {
headers: { "app-id": process.env.REACT_APP_API_KEY }
})
])
.then(
axios.spread((detail, comment) => {
console.log("before: ", comments)
console.log("data:", comment.data.data)
setComments([...comment.data.data])
console.log("after: ", comments)
})
)
.catch((detail_err, comment_err) => {
console.error(detail_err);
console.error(comment_err);
});
}, []);
return (
<div>
detail page:
</div>
);
};
export default PostDetail;
and got some data with axiosin useEffect hook, and used setComments() with the data(comment.data.data). but it doesn't set the axios data for some reason. What's wrong with it? If you help me out, it would be a huge help
The setter method (setComments) is asynchronous. Therefore you cannot expect to get the updated value to log right after it.
setComments([...comment.data.data])
console.log("after: ", comments)
You should move the log to the component's top level.
const PostDetail = () => {
const { id } = useParams();
let [comments, setComments] = useState([]);
useEffect(async () => {
await axios
.all([
axios.get(`https://dummyapi.io/data/v1/post/${id}`, {
headers: { "app-id": process.env.REACT_APP_API_KEY }
}),
axios.get(`https://dummyapi.io/data/v1/post/${id}/comment`, {
headers: { "app-id": process.env.REACT_APP_API_KEY }
})
])
.then(
axios.spread((detail, comment) => {
console.log("before: ", comments)
console.log("data:", comment.data.data)
setComments([...comment.data.data])
})
)
.catch((detail_err, comment_err) => {
console.error(detail_err);
console.error(comment_err);
});
}, []);
// move the log to here
console.log("after: ", comments)
return (
<div>
detail page:
</div>
);
};
export default PostDetail;
If you want to do some other work when comments gets changed, add a useEffect hook with comments as a dependancy.
useEffect(() => {
console.log(comments);
}, [comments]);

Call hook from another hook in react

I'm quite new to react and trying to ease some development.
I'm having this custom hook useApi.
import {useState} from "react";
import {PetsApiFactory} from "petstore-axios-api"
import {useKeycloak} from "#react-keycloak/web";
const petsApi = new PetsApiFactory({}, `${process.env.REACT_APP_BASE_URL}`);
export const useApi = () => {
const [isLoading, setIsLoading] = useState(false);
const {keycloak} = useKeycloak();
const createPets = (requestData) => {
setIsLoading(true);
return petsApi.createPets(requestData, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
}).finally(() => setIsLoading(false));
};
const listPets = (limit = undefined) => {
setIsLoading(false);
return petsApi.listPets(limit, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
}).finally(() => setIsLoading(false));
};
const showPetById = (petId) => {
setIsLoading(true);
return petsApi.showPetById(petId, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
}).finally(() => setIsLoading(false));
};
return {
createPets,
listPets,
showPetById,
isLoading
}
};
I'd like to call it from within another component like in this snippet.
useEffect(() => {
listPets()
.then(result => setPetsData(result.data))
.catch(console.log)
}, []);
However react is telling me that I'm missing the dependency on listPets
React Hook useEffect has a missing dependency: 'listPets'. Either include it or remove the dependency array
I've tried to include listPets as a dependency but that leads to repeatable call to backend service. What would be the best way to rewrite the call to useApi hook?
Thanks
Change component's useEffect:
useEffect(() => {
listPets()
.then(result => setPetsData(result.data))
.catch(console.log)
}, [listPets]);
And then try to wrap listPets function with useCallBack hook like this:
const showPetById = useCallback((petId) => {
setIsLoading(true);
return petsApi.showPetById(petId, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
}).finally(() => setIsLoading(false));
},[petsApi]);
you have forget to add listPets into useEffect
useEffect(() => {
listPets()
.then(result => setPetsData(result.data))
.catch(console.log)
}, [listPets]);

Resources