Infinite rendering of component while fetching data with useEffect hook in reactjs - reactjs

When i want to fetch collections from api, infinite rendering in useEffect happening on the console. How can fix it?
const Collections = () => {
const [collections, setCollections] = useState([]);
const token = window.localStorage.getItem("token");
useEffect(() => {
fetchUsers();
},[setCollections]);
const fetchUsers = async () => {
const response = await fetch(
"https://itransition-capstone.herokuapp.com/collections/allCollections",
{
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
}
);
const data = await response.json();
setCollections(data);
console.log("Collections", data);
};
};
export default Collections;

Related

Not able to implement data from one api used to get data from another

I am making a meme sharing app. In that app there are total 2 apis of getting memes.
One for memes by all the users another is only for individual user.
In second api I am able to get the data as the user id is from 3rd api.
from here i get the id of each individual.
function UserProfile({memeid}) {
const token = localStorage.getItem("token");
const [response, setResponse] = useState({});
const [id, setId] = useState('')
const userData = async() => {
await axios
.get("http://localhost:8081/userInfo/me", {
headers: { Authorization: `Bearer ${token}` },
Accept: "application/json",
"Content-Type": "application/json",
})
.then((res) => {
setResponse(res.data)
setId(res.data.id)
memeid = id
})
.catch((err)=>{
console.log(err)
})
}
console.log(id)
useEffect(()=>{
userData()
},[])
Now I want this to be used in in another api. for that is have written this code.
function MemeById({id}) {
const [response, setResponse] = useState([])
const token = localStorage.getItem("token");
// const id = "632a119672ba0e4324b18c7d"
const memes = async () => {
await axios
.get("http://localhost:8081/memes/" + id, {
headers: { Authorization: `Bearer ${token}` },
Accept: "application/json",
"Content-Type": "application/json",
})
.then((res) => {
const data = res.data;
setResponse(res.data)
console.log(data);
})
.catch((err) => {
alert(err);
console.log(err);
});
};
useEffect(()=>{
memes()
},[])
I am calling these two at User
function User() {
let id;
return (
<div>
<UserProfile memeid={id}/>
<MemeById id = {id} />
</div>
)
}
I am getting the error for this.
How to solve this error
You're making a big mistake. I think you should learn more about state and props in react.
Problem :
In your User component, you're creating a variable and passing that variable into two other component. You're trying to update the value of props from UserProfile and expecting that updated value in MemeById which is not going to work.
Solution :
function User() {
const [memeId, setMemeId] = useState(null);
return (
<div>
<UserProfile updateId={(newId) => setMemeId(newId)}/>
<MemeById memeId = {memeId} />
</div>
)
}
And in your UserProfile component
function UserProfile({updateId}) {
...
const userData = async() => {
...
// memeid = id
updateId(res.data.id)
...
}
In you MemeById component:
function MemeById({memeId}) {
...
// use memeId here
...
}

dataLoaded state only changed after page reloaded

I'm doing an API call to get some data. then I keep a useState called dataLoaded. on a successful API call I make the dataLoaded=true. but to see it changed I have to reload the page.
following is my code.
const [dataLoaded, setDataLoaded] = useState(false)
useEffect(() =>{
const url = `${process.env.REACT_APP_DEV_BASE_URL}/v1/movie/`+ path.eventId + `/venue/`+ path.venue +`/showtime`;
const requestOptions = (token) => {
return ({
method: 'GET',
headers: { 'Content-Type': 'application/json', 'client_token': '4ece-9e89-1b6d4d2cbb61' }
})
};
const fetchData = async () => {
try {
const response = await fetch(url, requestOptions());
const json = await response.json();
// console.log(json);
// console.log(json.data.venueDateShowtime)
setShowTimes(json.data.dateShowtimes[0].showtimes[0]);
console.log(json.data.dateShowtimes[0].date)
setShowdate(json.data.dateShowtimes[0].date);
setDataLoaded(true);
console.log(dataLoaded)
console.log(showTimes.showtimeId)
console.log(showdate)
if(dataLoaded){
getSeatsArrangement();
}
console.log('jjjj')
}
catch (error) {
console.log("error",error);
}
};
fetchData();
},[]);
const getSeatsArrangement = async () => {
const requestOptions = (token) => {
return ({
method: 'GET',
headers: { 'Content-Type': 'application/json', 'client_token': '4ece-9e89-1b6d4d2cbb61' }
})
};
console.log(showTimes.showtimeId)
console.log(showdate)
try{
const url = `${process.env.REACT_APP_DEV_BASE_URL}/v1/seat?venueId=` + path.venue + `&movieId=`+ path.eventId +`&showtimeId=1011&movieDate=2022-10-11`;
const response = await fetch(url,requestOptions());
const json = await response.json();
console.log(json)
setReservedSeats(json.data.reservedSeats.reservedSeat)
setNonReservedSeats(json.data.reservedSeats.nonReservedSeats)
console.log(reservedSeats)
console.log(nonReservedSeats)
} catch(error) {
console.log("error",error);
}
}
Console logs when page loads
What is the aim of the code? fetchData is performed once after page loading (because of using ,[] at the end of useeffect.
And a remark: If you log your state right after setting it, the previous value will be shown! you should define another useeffect with your state as dependency (for each state) and log your state in there.
useEffect(() => {
console.log(dataLoaded)
if(dataLoaded){
getSeatsArrangement();
}
console.log('jjjj')
}, [dataLoaded]);
useEffect(() => {
console.log(showTimes.showtimeId)
}, [showTimes]);
useEffect(() => {
console.log(showdate)
}, [showdate]);
useEffect(() =>{
const url = `${process.env.REACT_APP_DEV_BASE_URL}/v1/movie/`+ path.eventId + `/venue/`+ path.venue +`/showtime`;
const requestOptions = (token) => {
return ({
method: 'GET',
headers: { 'Content-Type': 'application/json', 'client_token': '4ece-9e89-1b6d4d2cbb61' }
})
};
const fetchData = async () => {
try {
const response = await fetch(url, requestOptions());
const json = await response.json();
// console.log(json);
// console.log(json.data.venueDateShowtime)
setShowTimes(json.data.dateShowtimes[0].showtimes[0]);
console.log(json.data.dateShowtimes[0].date)
setShowdate(json.data.dateShowtimes[0].date);
setDataLoaded(true);
}
catch (error) {
console.log("error",error);
}
};
fetchData();
},[]);

Why I need to refresh manually to fetch data api in react?

I make react app using react router v5, and axios as api instance. I fetch the data in AppRouter file.
Here is my approuter.tsx
const AppRouter = () => {
const dispatch = useAppDispatch();
const token = useAppSelector((state) => state.user.token);
const getUser = useCallback(async () => {
const { data } = await Services.getUser();
dispatch(userAction.setUser(data));
}, [dispatch]);
useEffect(() => {
const localStorage = new LocalStorageWorker();
const storageToken = localStorage.get('token');
dispatch(userAction.setToken(storageToken));
}, [dispatch]);
useEffect(() => {
if (token) {
getUser();
console.log('Worked');
}
}, [token, getUser]);
return (
...
)
}
Actually the function work properly, but I need to refresh the page manually to run these functions. How can I make the function run without refreshing the page?
Update:
The problem is because my axios create instance. I should use interceptors to keep the data fetching in useEffect.
My instance looks like this:
(before update)
const token = localStorage.get('token');
const createInstance = () => {
const instance = axios.create({
baseURL: BASE_URL,
headers: {
'content-type': 'application/json',
Accept: 'application/json',
},
});
instance.defaults.headers.common.Authorization = `Bearer ${token}`;
return instance;
};
(after update)
const createInstance = () => {
const instance = axios.create({
baseURL: BASE_URL,
headers: {
'content-type': 'application/json',
Accept: 'application/json',
},
});
instance.interceptors.request.use(
(config) => {
const token = window.localStorage.getItem('token');
if (token) {
return {
...config,
headers: { Authorization: `Bearer ${token}` },
};
}
return null;
},
(err) => Promise.reject(err)
);
return instance;
};
And now the data fetching is work properly. Thank you

useSWR not picking updated state variable

The token variable is not updating in SWR as I update it via useState in revalidate function.
const [token, setToken] = useState('')
console.log(token) // this updates as setToken is called
const fetcher = (url) => {
console.log(token) // this remains empty although it re-renders
return axios.get(
url,
{
headers: {
'Authorization': `Bearer ${token}`,
},
},
)
.then(res => res.data)
.catch(error => {//whatever})
}
const { data: user, error, revalidate } = useSWR('_ENDPOINT_', fetcher)
const login = (email, password) => {
axios.post('/login', {email, password})
.then((response) => {
setToken(response.data.token)
revalidate()
})
}
I end up using:
'Authorization': `Bearer ${localStorage.getItem('_token').
replace(/['"]+/g, '')}`,
'Authorization': `Bearer ${token}` // instead of this using from useState which didn't update in ages
Because it appears localStorage was available after useSWR init; (even on page refreshes)
You have to use useCallback on the fetcher function for it to properly pick up the token state variable change.
const fetcher = useCallback(
(url) => {
console.log(token) // Will log the updated `token` value
return axios
.get(
url,
{ headers: { 'Authorization': `Bearer ${token}` } }
)
.then(res => res.data)
.catch(error => {/*whatever*/})
},
[token]
)
A better solution, to avoid using useCallback, would be to move the fetcher function outside the component, and pass several arguments to the fetcher in the useSWR call. This has the benefit of using the token as the key for the request (in addition to the URL, making the caching more specific), and only making the request when token is defined.
const fetcher = (url, token) => {
return axios
.get(
url,
{ headers: { 'Authorization': `Bearer ${token}` } }
)
.then(res => res.data)
.catch(error => {/*whatever*/})
}
const SomeComponent = () => {
const [token, setToken] = useState('')
const { data: user, error, revalidate } = useSWR(token ? ['_ENDPOINT_', token] : null, fetcher)
// Rest of the component
}

Async react hook

I would like create async React hook in React-Native for get AsyncStorage data before run my fetch.
Example :
const useCallApi = async url => {
const [instance, token] = await Promise.all([
AsyncStorage.getItem('instance'),
AsyncStorage.getItem('token')
]);
const data = useFetch(`${instance}/api/v1/${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
return data;
};
export default useCallApi;
But this hook return an error Unhandled promise rejection. I think the problem is await before useFetch hook, but how i can fix this case ?
If anyone can help me :)
Thank you community,
Why not use AsyncStorage in another way? Only after initialization is complete, you can access AsyncStorage synchronously from anywhere.Through react-native-easy-app, you can operate AsyncStorage like this
import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';
export const RNStorage = {
token: undefined,
isShow: undefined,
userInfo: undefined
};
const initCallback = () => {
// From now on, you can write or read the variables in RNStorage synchronously
// equal to [console.log(await AsyncStorage.getItem('isShow'))]
console.log(RNStorage.isShow);
// equal to [ await AsyncStorage.setItem('token',TOKEN1343DN23IDD3PJ2DBF3==') ]
RNStorage.token = 'TOKEN1343DN23IDD3PJ2DBF3==';
// equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
RNStorage.userInfo = {name: 'rufeng', age: 30};
};
XStorage.initStorage(RNStorage, AsyncStorage, initCallback);
Maybe adding await before AsyncStorage helps you:
const useCallApi = async url => {
const [instance, token] = await Promise.all([
await AsyncStorage.getItem('instance'),
await AsyncStorage.getItem('token')
]);
const data = useFetch(`${instance}/api/v1/${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
return data;
};
export default useCallApi;
const useCallApi = async url => {
let instance = null;
let token = null;
Promise.all([
AsyncStorage.getItem('instance'),
AsyncStorage.getItem('token')
]).then(d=>{
instance = d[0];
token = d[1];
}).catch(e=>throw e);
const data = useFetch(`${instance}/api/v1/${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
return data;
};
export default useCallApi;
I think a promise needs then when it resolve and a catch for error catching
You should not implement the hooks like that, it will cause call fetch many times whenever the component which uses this hook re-render.
Try this instead:
const useCallApi = url => {
const [data, setData] = useState(null)
useEffect(() =>{
const fetchData = async () =>{
const [instance, token] = await Promise.all([
AsyncStorage.getItem('instance'),
AsyncStorage.getItem('token')
]);
// I assume that your useFetch is a hook that retruen a Promise of data
const fetchedData = await useFetch(`${instance}/api/v1/${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
setData(fetchedData)
}
fetchData()
},[url])
return data;
};

Resources