I'm sure this has to do with the timing geolocation and axios fetching the data. With useEffect
it will give the wrong location. If I call the function without useEffect I will get the correct location but this creates an infinite loop. I've also tried using a condition in the useEffect.
and have tried using setTimeout;
How do I make sure navigator.geolocation has the lat and long before fetching the data?
import React from 'react';
import { useState, useEffect } from 'react';
import './css/weather.css'
import axios from 'axios';
const Weather = () => {
const [lat, setLat] = useState(0);
const [long, setLong] = useState(0);
const [temp, setTemp] = useState(0);
const [wind, setWind] = useState('');
const [windDir, setWindDir] = useState('');
const [gust, setGust] = useState('');
const [precip, setPrecip] = useState(0);
const [icon, setIcon] = useState('');
const [pic, setPic] = useState('');
const [city, setCity] = useState('');
const getWeather = async () => {
try {
await navigator.geolocation.getCurrentPosition((position) => {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
})
const response = await axios.get(`http://api.weatherapi.com/v1/current.json?key=6e6263afb84f44279f731543222510&q=${lat},${long}&aqi=no`);
console.log(response.data)
setCity(response.data.location.name)
setTemp(response.data.current.temp_f);
setWind(response.data.current.wind_mph);
setWindDir(response.data.current.wind_dir);
setGust(response.data.current.gust_mph);
setPrecip(response.data.current.precip_in);
setPic(response.data.current.condition.icon.slice(-7))
if(response.data.current.condition.icon.includes('day')){
setIcon(<img src={`/assets/weather/64x64/day/${pic}`}></img>
)
} else {
setIcon(<img src={`/assets/weather/64x64/night/${pic}`}></img> )
}
} catch (err) {
console.error(err)
}
}
useEffect(() =>{
getWeather();
},[])
return (
<div className='weather-container'>
<p>{icon}</p>
<h2>{city}</h2>
<p>{`Current Temp: ${Math.round(temp)} °F`}</p>
<p>{`Wind: ${windDir} ${Math.round(wind)} mph Gusts: ${Math.round(gust)} mph`}</p>
<p>{`Precip: ${Math.round(precip)}in`}</p>
</div>
)
}
export default Weather;
I think I have found the solution. Putting lat in useEffect as an dependency seems to have solved the problem.
useEffect(() => {
getWeather();
}, [lat]);
const DashboardPage = (props) => {
const userData = useSelector((state) => state.userDataReducer.user_record_reducer)
console.log('userdata', userData);
const adm_id = userData.adm_id
const [vistLoader, setVisitLoader] = useState(false)
const drid = localStorage.getItem('drId')
const [dashboard, setDashboard] = useState({})
const [collapsed,setCollapased] = useState(false);
const mr = userData.mrno
useEffect(()=>{
setVisitLoader(true)
const dashboard = getPatientDashboard({mr,drid}).then(res =>{
setDashboard(res)
setTimeout(()=>{
setVisitLoader(false)
}, 500)
}).catch(err =>{
console.log('err',err);
})
},[])
My custom hook fetches data asynchronously. When it is used in a component, returned value doesn't get updated. It keeps showing default value. Does anybody know what is going on? Thank you!
import React, {useState, useEffect} from 'react'
import { getDoc, getDocs, Query, DocumentReference, deleteDoc} from 'firebase/firestore'
export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
const [value, setValue] = useState<T|undefined>(undefined)
const [isLoading, setIsLoading] = useState<boolean>(true)
const update = async () => {
const docSnap = await getDoc(docRef)
if (docSnap.exists()) {
const data = docSnap.data()
setValue(data)
}
setIsLoading(false)
}
useEffect(() => {
update()
}, [])
console.log(value, isLoading) // it can shows correct data after fetching
return {value, isLoading}
}
import { useParams } from 'react-router-dom'
const MyComponent = () => {
const {userId} = useParams()
const docRef = doc(db, 'users', userId!)
const {value, isLoading} = useFirestoreDocument(docRef)
console.log(value, isLoading) // keeps showing {undefined, true}.
return (
<div>
...
</div>
)
}
It looks like youe hook is only being executed once upon rendering, because it is missing the docRef as a dependency:
export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
const [value, setValue] = useState<T|undefined>(undefined)
const [isLoading, setIsLoading] = useState<boolean>(true)
useEffect(() => {
const update = async () => {
const docSnap = await getDoc(docRef)
if (docSnap.exists()) {
const data = docSnap.data()
setValue(data)
}
setIsLoading(false)
}
update()
}, [docRef])
console.log(value, isLoading) // it can shows correct data after fetching
return {value, isLoading}
}
In addition: put your update function definition inside the useEffect hook, if you do not need it anywhere else. Your linter will complaing about the exhaustive-deps rule otherwise.
The useEffect hook is missing a dependency on the docRef:
export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
const [value, setValue] = useState<T|undefined>(undefined);
const [isLoading, setIsLoading] = useState<boolean>(true);
useEffect(() => {
const update = async () => {
setIsLoading(true);
try {
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
const data = docSnap.data();
setValue(data);
}
} catch(error) {
// handle any errors, log, etc...
}
setIsLoading(false);
};
update();
}, [docRef]);
return { value, isLoading };
};
The render looping issue is because docRef is redeclared each render cycle in MyComponent. You should memoize this value so a stable reference is passed to the useFirestoreDocument hook.
const MyComponent = () => {
const {userId} = useParams();
const docRef = useMemo(() => doc(db, 'users', userId!), [userId]);
const {value, isLoading} = useFirestoreDocument(docRef);
console.log(value, isLoading);
return (
<div>
...
</div>
);
};
Please tell me how to fetch new data into the array using useState. Each time useEffect is using, the array is initialized empty again. I need to pass this array to the table so that when the values change, the table is rendered again
const AccountContainer = () => {
let [isLoaded, setIsLoaded] = useState(false);
let [page, setPage] = useState(0);
let [accData, setAccData] = useState([]);
useEffect(() => {loadData()}, [page]);
const loadData = async () => {
const response = await fetch(API_URL);
const data = await response.json();
data.content.map(acc => {
setAccData([...accData, acc])
});
setIsLoaded(true);
};
return (
<table data={accData} />
)
};
export default AccountContainer;
added log
[] AccountContainer.js:30
[] AccountContainer.js:30
[] AccountContainer.js:30
[] AccountContainer.js:30
[] AccountContainer.js:30
[] AccountContainer.js:30
https://codesandbox.io/embed/great-rubin-poywh?fontsize=14&hidenavigation=1&theme=dark
Thanks in advance
here is the solution in the sandbox
// AccountList.jsx
import React from "react";
const AccountList = props => (
<div>
{console.warn(props)}
{props.dataAcc.map(acc => (
<div>{acc.email}</div>
))}
)
</div>
);
export default AccountList;
App.jsx
import React, { useState, useEffect } from "react";
import AccountList from "./AccountList";
import "./styles.css";
const AccountContainer = () => {
let [isLoaded, setIsLoaded] = useState(false);
let [page, setPage] = useState(0);
let [accData, setAccData] = useState([]);
const API_URL = `https://reqres.in/api/users?page=1`;
useEffect(() => {
loadData();
}, [page]);
const loadData = async () => {
const response = await fetch(API_URL);
const data = await response.json();
setAccData([...accData, ...data.data]);
setIsLoaded(true);
};
const renderTable = () => {
if (isLoaded) {
return (
<div>
{console.warn(accData)}
<AccountList dataAcc={accData} />
</div>
);
} else if (!isLoaded) return "Loading";
};
return renderTable();
};
export default AccountContainer;
I want to build a loading spinner while request API with React, I use context in React to fetch API and make a isLoading state in the context but when I call isLoading in App.js. It have an error TypeError: Cannot destructure property 'isLoading' of 'Object(...)(...)' as it is undefined.
the context.js
/* eslint-disable react/prop-types */
/* eslint-disable no-underscore-dangle */
import React, { useState, useEffect, createContext, useCallback } from 'react';
import moment from 'moment';
import { getResource } from '../api/resourceApi';
import { getProject } from '../api/projectApi';
import { getBooking, deleteBooking, updateBooking } from '../api/bookingApi';
import { HEIGHT_BOOKING } from '../containers/App/constant';
import { compareByDay, getNumberOfDay, isWeekend } from '../utils/Date';
const CalendarContext = createContext();
const CalendarProvider = props => {
const [isDragLoading, setIsDragLoading] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [persons, setPersons] = useState([]);
const [projects, setProjects] = useState([]);
const [search, setSearch] = useState('');
const [searchResult, setSearchResult] = useState([]);
const [bookings, setBookings] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [startDay, setStartDay] = useState(moment('2019-12-30', 'YYYY-MM-DD'));
const [endDay, setEndDay] = useState(moment('2020-02-03', 'YYYY-MM-DD'));
const [disabled, setDisabled] = useState(false);
const [isHoverWorking, setIsHoverWorking] = useState(true);
const [addBookingStatus, setAddBookingStatus] = useState(true);
const [content, setContent] = useState({
resource: [],
booking: undefined,
startDate: moment(),
endDate: moment(),
});
const [start, setStart] = useState(0);
const [end, setEnd] = useState(0);
const [selecting, setSelecting] = useState(false);
const [resourceStart, setResourceStart] = useState(0);
const [first, setFirst] = useState(false);
const [startCellDay, setStartCellDay] = useState(moment());
const [lastDate, setLastDate] = useState(0);
const [isHover, setIsHover] = useState(false);
const [formIsOpening, setFormIsOpening] = useState(false);
const [bookingId, setBookingId] = useState(null);
const contentGlobal = () => content;
const setContentGlobal = newContent => {
setContent(newContent);
};
const hoverObject = {
start,
end,
selecting,
resourceStart,
first,
startCellDay,
lastDate,
isHover,
formIsOpening,
isHoverWorking,
addBookingStatus,
};
const onDisabled = () => {
setDisabled(!disabled);
};
const fetchResource = async () => {
setIsLoading(true);
const res = await getResource();
const result = res.data.resources;
const personsFilter = result.map(resource => {
const person = {
_id: resource._id,
name: `${resource.name.first} ${resource.name.last}`,
avatar: resource.avatar,
};
return person;
});
setPersons(personsFilter);
setSearchResult(personsFilter);
setIsLoading(false);
};
const fetchProject = async () => {
setIsLoading(true);
const res = await getProject();
const result = res.data.projects;
setProjects(result);
setIsLoading(false);
};
const fetchBooking = useCallback(async () => {
setIsLoading(true);
const res = await getBooking(startDay, endDay);
const result = res.data.bookings;
const bookingsConvert = result.map(booking => {
const schedule = {
...booking,
project: booking.project,
startDay: moment(booking.startDay),
endDay: moment(booking.endDay),
};
return schedule;
});
setBookings([...bookingsConvert]);
setIsLoading(false);
}, [startDay, endDay]);
const removeBooking = async id => {
const newBookings = bookings.filter(booking => booking._id !== id);
await deleteBooking(id);
setBookings([...newBookings]);
};
const handleSetBookings = (booking, onAdd = true) => {
if (onAdd) {
setBookings([...bookings, booking]);
} else {
const filterBooking = bookings.filter(e => e._id !== booking._id);
setBookings([...filterBooking, booking]);
}
};
const updateSearch = event => {
setSearch(event.target.value.toLowerCase());
};
function getBookingWithResource(date, indexResource) {
const bookingsWithResource = bookings
.filter(booking => {
const isBookingBelongResource =
booking.startDay.isSame(date, 'day') &&
booking.resourceId === searchResult[indexResource]._id;
return isBookingBelongResource;
})
.sort(
// eslint-disable-next-line no-shadow
(first, second) => compareByDay(second.endDay, first.endDay),
);
return bookingsWithResource;
}
useEffect(() => {
fetchResource();
return () => {};
}, []);
useEffect(() => {
fetchBooking();
return () => {};
}, [fetchBooking]);
useEffect(() => {
fetchProject();
return () => {};
}, []);
useEffect(() => {
const filteredResults = persons.filter(
item => item.name.toLowerCase().indexOf(search) !== -1,
);
setSearchResult(filteredResults);
}, [search, persons]);
return (
<CalendarContext.Provider
value={{
persons,
projects,
search,
searchResult,
updateSearch,
bookings,
setBookings,
fetchBooking,
handleSetBookings,
getMaxTotalOverlapBooking,
getBookingWithResource,
getMarginTopBooking,
isModalOpen,
handleCloseModal,
setStartDay,
setEndDay,
startDay,
endDay,
disabled,
onDisabled,
removeBooking,
setBegin,
updateOnDidDragBooking,
contentGlobal,
setContentGlobal,
start,
setStart,
end,
setEnd,
selecting,
setSelecting,
resourceStart,
setResourceStart,
first,
setFirst,
startCellDay,
setStartCellDay,
lastDate,
setLastDate,
isHover,
setIsHover,
addBookingStatus,
setAddBookingStatus,
formIsOpening,
hoverObject,
hoverSetObject,
isDragLoading,
setIsDragLoading,
bookingId,
isLoading,
}}
>
{props.children}
</CalendarContext.Provider>
);
};
export { CalendarProvider, CalendarContext };
App.js
import './App.css';
import './containers/Header/Header.css';
import React, { useContext, useEffect, useState } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { ThemeProvider } from 'styled-components';
import { theme } from './containers/common/AppStyle';
import Header from './components/Header';
import Dashboard from './containers/DashboardPage';
import { CalendarContext } from './context/Calendar';
const App = () => {
const context = CalendarContext;
const { isLoading } = useContext(context);
console.log(isLoading);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
}, []);
if (loading) return null;
return (
<ThemeProvider theme={theme}>
<Router>
<Header />
<Switch>
<Route path="/dashboard" component={Dashboard} />
</Switch>
</Router>
</ThemeProvider>
);
};
export default App;
change:
value={{
isLoading,
}}
with
value={isLoading}