I am trying to use React Hooks but somehow my state is not updating
const [Choices, setChoices] = useState([]);
useEffect(() => {
async function getUsers() {
// Here’s the magic
let tempChoices = [];
const promises = MAINLIST.List.ChoicesFields.map(async z => {
tempChoices[z] = [];
let GetChoicesFromInternalFieldResults = await GetChoicesFromInternalField(
z
);
GetChoicesFromInternalFieldResults.map(c => {
tempChoices[z].push({
key: c,
text: c,
value: c
});
});
});
await Promise.all(promises);
const object = { Choices: tempChoices };
// THIS IS PRINTING CORRECT VALUES
console.log(object);
setChoices(object);
}
getUsers();
}, []);
Here is the console.log result
but when I check the state in the developer tool the state is empty
I found my answers. I made "tempChoices" into an array instead of an object.
const [Choices, setChoices] = useState({});
useEffect(() => {
async function getUsers() {
// Here’s the magic
let tempChoices = {};
const promises = MAINLIST.List.ChoicesFields.map(async z => {
tempChoices[z] = [];
let GetChoicesFromInternalFieldResults = await GetChoicesFromInternalField(
z
);
GetChoicesFromInternalFieldResults.map(c => {
tempChoices[z].push({
key: c,
text: c,
value: c
});
});
});
await Promise.all(promises);
const object = { Choices: tempChoices };
setChoices(object);
}
getUsers();
}, []);
Related
I have a simple React App using Firestore.
I have a document in Firestore:
{
date: November 20, 2022 at 11:24:44 AM UTC+1,
description: "...",
title: "Dummy title",
type: "student",
userRef: /users/0AjB4yFFcIS6VMQMi7rUnF3eJXk2
}
Now I have a custom hook, that fetches the data:
export const useAnnouncements = () => {
const [announcements, setAnnouncements] = useState([]);
useEffect(() => {
getAnnouncements().then((documents) => {
const documentsList = [];
documents.forEach((doc) => {
const document = { id: doc.id, ...doc.data() };
getUser(document.userRef).then((u) => {
document.user = u.data(); // <-- HERE is problem
});
documentsList.push(document);
setAnnouncements(documentsList);
});
});
}, []);
return [announcements];
};
Problem is that I have a REFERENCE field type, and it has to be fetched separately. Result? My list is populated, but first without user. Later, when the users' data is fetched, the state is not being updated.
How to deal with React + Firestore's reference field?
Array.prototype.forEach is not designed for asynchronous code. (It was not suitable for promises, and it is not suitable for async-await.) instead you can use map.
useEffect(() => {
getAnnouncements().then((documents) => {
const promises = documents.map((doc) => {
return getUser(doc.userRef).then((u) => {
const document = { id: doc.id, user: u.data(), ...doc.data() };
return document;
});
});
Promise.all(promises).then((documentsList) => {
setAnnouncements(documentsList);
});
});
}, []);
I think you need to wait for all the data to be fetched
export const useAnnouncements = () => {
const [announcements, setAnnouncements] = useState([]);
useEffect(() => {
let isValidScope = true;
const fetchData = async () => {
const documents = await getAnnouncements();
if (!isValidScope) { return; }
const allPromises = documents?.map(doc => {
return getUser(doc.userRef)
.then(user => {
return {
id: doc.id,
...doc.data(),
user: user.data()
}
})
}
const documentsList = await Promise.all(allPromises);
if (!isValidScope) { return; }
setAnnouncements(documentsList);
}
fetchData()
return () => { isValidScope = false }
}, []);
return [announcements];
};
Hope it helps in some way
I have react app requeting a flask server, I can return the objects but when I assing the state to a new variable it log undefined, even though I am able to log it
const [characters, setCharacters] = useState([]);
useEffect(() => {
const getData = async () => {
await fetch("http://127.0.0.1:6789/api/load_img_data")
.then((res) => res.json())
.then(
(result) => {
const arrayOfObj = Object.entries(result.imgData).map((e) => e[1]);
setCharacters(arrayOfObj);
},
(error) => {}
);
};
getData();
}, []);
console.log(characters); ## it works fine and log the object on the console
const columnsFromBackend = {
["1"]: {
name: "Terminator Group",
items: characters, ## doesn't work when I map over it as it will be always empty
}
}
so my question what it the best way to assign a state to variable? thanks
You can declare your columnsFromBacked and initialize it as empty object.After you data from api is stored in the hook, then you can assign the appropriate values to columnsFromBacked
Solution 1
let columnsFromBackend= {}
const [characters, setCharacters] = useState([]);
useEffect(() => {
const getData = async () => {
await fetch("http://127.0.0.1:6789/api/load_img_data")
.then((res) => res.json())
.then(
(result) => {
const arrayOfObj = Object.entries(result.imgData).map((e) => e[1]);
setCharacters(arrayOfObj);
columnsFromBackend = {
["1"]: {
name: "Terminator Group",
items: characters
}
}
},
(error) => {}
);
};
getData();
}, []);
}
Solution 2
You can implement useEffect hook with dependency on character hook.
sample code
useEffect(()=>{
columnsFromBackend = {...} //your code
}, [character]);
Im working on a star wars api app. I am getting an array of people objects, 10 characters. Who all are their own object with different values. However homeworld, and species are urls. So I have to fetch them and store that data to the correct place. I figured out a way to get the homeworld values to each character. However when I try to do it with species I receive undefined. Would appreciate any help this has been kind of a pain thanks ahead of time !
const [people, setPeople] = useState([]);
const [homeWorld, setHomeWorld] = useState([]);
const [species, setSpecies] = useState([]);
const [nextPageUrl, setNextPageUrl] = useState("https://swapi.dev/api/people/");
const [backPageUrl, setBackPageUrl] = useState('');
const [test, setTest] = useState([]);
const fetchPeople = async () => {
const { data } = await axios.get(nextPageUrl);
setNextPageUrl(data.next);
setBackPageUrl(data.previous);
return data.results;
}
const backPage = async () => {
const { data } = await axios.get(backPageUrl);
setCharacters(data.results);
setNextPageUrl(data.next);
setBackPageUrl(data.previous);
}
// Get People
async function getPeople() {
const persons = await fetchPeople();
const homeWorldUrl= await Promise.all(
persons.map((thing) => axios.get(thing.homeworld)),
);
const newPersons = persons.map((person) => {
return {
...person,
homeworld: homeWorldUrl.find((url) => url.config.url === person.homeworld)
};
});
const newPersons2 = newPersons.map((person) => {
return {
...person,
homeWorld: person.homeworld.data.name
};
});
setPeople(newPersons2);
}
// Get Species
async function getSpecies() {
const persons = await fetchPeople();
const speciesUrl = await Promise.all(
persons.map((thing) => axios.get(thing.species)),
);
const newSwapi = persons.map((person) => {
return {
...person,
species: speciesUrl.find((info) => info.data.url === person.species)
};
});
setTest(newSwapi);
// const newPersons2 = newPersons.map((person) => {
// return {
// ...person,
// homeWorld: person.homeworld.data.name
// };
// });
}
useEffect(() => {
getPeople();
getSpecies();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); ```
Species property of person is a array, so your getSpecies() should be like
async function getSpecies() {
const persons = await fetchPeople();
const speciesUrl = await Promise.all(
persons
.filter((thing) => thing.species.length)
.map((thing) => axios.get(thing.species[0]))
);
const newSwapi = persons.map((person) => {
return {
...person,
species: speciesUrl.find((info) => info.data.url === person.species[0])
};
});
setTest(newSwapi);
}
I made this function that updates with a callback function when the firestore database changes.
export function realTimeData(collectionName, callback, queryForced) {
const dbRef = collection(db, collectionName);
const queryOrdered = query(
collection(db, collectionName),
orderBy("time", "desc"),
limit(50)
);
const unsub = onSnapshot(queryForced || queryOrdered, (snapshot) => {
const result = [];
snapshot.docs.forEach((doc) => {
result.push({
id: doc.id,
...doc.data(),
});
});
unsubs.push(unsub);
console.log("realTimeData", result);
callback({ [collectionName]: result });
});
}
Now I was wondering if this is correct? Does it always update the data when I unmount the react component?
My react component below:
function App() {
const [history, setHistory] = useState({ ttd: [] });
useEffect(() => {
realTimeData("ttd", setHistory);
}, []);
// etc ..
I'm learning to React Hooks.
And I'm struggling initialize data that I fetched from a server using a custom hook.
I think I'm using hooks wrong.
My code is below.
const useFetchLocation = () => {
const [currentLocation, setCurrentLocation] = useState([]);
const getCurrentLocation = (ignore) => {
...
};
useEffect(() => {
let ignore = false;
getCurrentLocation(ignore);
return () => { ignore = true; }
}, []);
return {currentLocation};
};
const useFetch = (coords) => {
console.log(coords);
const [stores, setStores] = useState([]);
const fetchData = (coords, ignore) => {
axios.get(`${URL}`)
.then(res => {
if (!ignore) {
setStores(res.data.results);
}
})
.catch(e => {
console.log(e);
});
};
useEffect(() => {
let ignore = false;
fetchData(ignore);
return () => {
ignore = true;
};
}, [coords]);
return {stores};
}
const App = () => {
const {currentLocation} = useFetchLocation();
const {stores} = useFetch(currentLocation); // it doesn't know what currentLocation is.
...
Obviously, it doesn't work synchronously.
However, I believe there's the correct way to do so.
In this case, what should I do?
I would appreciate if you give me any ideas.
Thank you.
Not sure what all the ignore variables are about, but you can just check in your effect if coords is set. Only when coords is set you should make the axios request.
const useFetchLocation = () => {
// Start out with null instead of an empty array, this makes is easier to check later on
const [currentLocation, setCurrentLocation] = useState(null);
const getCurrentLocation = () => {
// Somehow figure out the current location and store it in the state
setTimeout(() => {
setCurrentLocation({ lat: 1, lng: 2 });
}, 500);
};
useEffect(() => {
getCurrentLocation();
}, []);
return { currentLocation };
};
const useFetch = coords => {
const [stores, setStores] = useState([]);
const fetchData = coords => {
console.log("make some HTTP request using coords:", coords);
setTimeout(() => {
console.log("pretending to receive data");
setStores([{ id: 1, name: "Store 1" }]);
}, 500);
};
useEffect(() => {
/*
* When the location is set from useFetchLocation the useFetch code is
* also triggered again. The first time coords is null so the fetchData code
* will not be executed. Then, when the coords is set to an actual object
* containing coordinates, the fetchData code will execute.
*/
if (coords) {
fetchData(coords);
}
}, [coords]);
return { stores };
};
function App() {
const { currentLocation } = useFetchLocation();
const { stores } = useFetch(currentLocation);
return (
<div className="App">
<ul>
{stores.map(store => (
<li key={store.id}>{store.name}</li>
))}
</ul>
</div>
);
}
Working sandbox (without the comments) https://codesandbox.io/embed/eager-elion-0ki0v