I am new to React and I am working on a simple project wherein I have an api with some data where that data changes every 2-3 seconds. Till now I was doing something like this :
const [Data, setData] = useState([]);
const[Data,setData] = useState([])
useEffect(() => {
fetch('api-url').then((result) => {
result.json().then((resp) => {
setData(resp);
setLoading(false)
})
})
}, [Data])
This was working fine for me as the data displayed on the webpage was also refresing automatically everytime the data changed in api also on removing Data from dependency it wouldnt update automatically.
I wanted to know whether it is a good practice or even a working method(data might be updating due to some other thing) to get new data . Also some part of my code that is related to this data(Eg. in one component I send the data that matches the user search) in that component the data does not update automatically so how do I solve that.
function handleAddFavourite(name) {
const newFav = Object.values(Data).find(e => e.name === name)
if (favourites.find((n) => n.name === name)) {
const newFavList = favourites.filter((n) => n.name !== name)
setFavourites(newFavList)
}
else {
const newFavList = [...favourites, newFav];
setFavourites(newFavList)
console.log(newFavList)
}
}
Here the add to fav. functionality works correctly. I send this fav list to another component . But in that component the values in data doesnt update automatically even when they are being updated in api data.
Any help would be appreciated.
Related
I have a very simple react code, which I use to track containers location on a territory. After a new container get's into the territory I have props.operationsList changed. So I send get response to server API when props.operationsList changes
useEffect(() => {
async function fetchContainerLocation() {
const response = await CoordinatesService.getContainersPosition()
console.log('response = ', response.data.features)
setContainersList(response.data.features)
console.log('containersList = ', containersList)
}
fetchContainerLocation()
}, [props.operationsList])
I need to update containersList const, that I use to rerender a map API where I should locate the containers. I define it like that:
const [containersList, setContainersList] = useState([])
I need to set containersList in accordance with that response fron server (response.data.features) to make my map rerender. What's strange,
console.log('response = ', response.data.features)
shows accurate and correct data from server, but the next
console.log('containersList = ', containersList)
is not equal with this response
Instad of getting the map rendered with the right data from server response, I have wrong data. So, I do now understand why such an straightforward approch do not work and how to fix it
State updates in React are asynchronous; when an update is requested, there is no guarantee that the updates will be made immediately.
So, try to log your containersList outside useEffect and compare both logs. both should be same.
TIP: While using map method with your containerList use it like containerList?.map() so that page does not turn out to be blank.
const fetchContainerLocation = async () => {
const response = await CoordinatesService.getContainersPosition();
console.log("response = ", response.data.features);
setContainersList(response.data.features);
};
useEffect(() => {
fetchContainerLocation();
}, [props.operationsList]);
console.log(containerList);
return (
<>
{containerList?.map((container) => (
<p>something you want to render</p>
))}
</>
);
No idea why, but it worked when I changed response.data.features
to [...response.data.features]
Working code
useEffect(() => {
async function fetchContainerLocation() {
setContainersList([])
const response = await CoordinatesService.getContainersPosition()
setContainersList([...response.data.features])
}
fetchContainerLocation()
}, [props.operationsList])
If anybody could explain why, it would be useful
Currently I'm building a pusher chat app with react. I'm trying to keep a list of online users. I'm using this code below:
const [users, setUsers] = useState([]);
useEffect(() => { // UseEffect so only called at first time render
window.Echo.join("server.0")
.here((allUsers) => {
let addUsers = [];
allUsers.map((u) => {
addUsers.push(u.name)
})
setUsers(addUsers);
})
.joining((user) => {
console.log(`User ${user.name} joined`);
setUsers([users, user]);
})
.leaving((user) => {
console.log(`User ${user.name} left`);
let addUsers = users;
addUsers.filter((u) => {
return u !== user.name;
})
setUsers(addUsers);
})}, []);
Whenever I subscribe to the pusher channel, I receive the users that are currently subscribed and the state is set correctly. All subscribed users are showing. However when a new user joins/leaves, the .joining/.leaving method is called and the users state is empty when I console log it. This way the users state is being set to only the newly added user and all other users are being ignored. I'm new to react so there is probably a simple explanation for this. I was not able to find the answer myself tough. I would really appreciate your help.
I saw the problem in joining. You need to update setState like this: setUsers([...users, user.name]);
And leaving also need to update:
const addUsers = users.filter((u) => {
return u !== user.name;
});
setUsers(addUsers);
here should also rewrite:
let addUsers = allUsers.map((u) => u.name);
setUsers(addUsers);
I found the issue. The problem is that when accessing state from within a callback funtion, it always returns the initial value. In my case an empty array. It does work when using a reference variable. I added the following lines:
const [users, _setUsers] = useState([]);
const usersRef = React.useRef(users);
const setUsers = data => {
usersRef.current = data;
_setUsers(data);
}
Each time I update the users state, I use the setUsers function. Now when I acces the state from inside my callback function with usersRef.current I get the latest state.
Also I used the code from the answer of #Viet to update the values correctly.
I am developing a chat application in React and firebase firestore. I want to display an unread messages badge every time a new message is added to the database. Currently, I am using a useEffect hook to check if the last message is read, and that only works only the first time the page renders - the unread messages badge appears only after I reload the page. I think what I can't figure out is how to re-render every time the state changes. Kindly look at my code and tell me what I am missing. I hope my issue is clear. Thanks in advance.
const [chatMessages, setChatMessages] = useState([])
const [isRead, setIsRead] = useState(false)
const readsRef = db.collection('rooms').doc(id).collection('messages')
useEffect(() => {
const unsubscribe = readsRef.orderBy('timestamp', 'desc')
.limit(1).onSnapshot((snapshot) => {
snapshot.docs.map((snap) => {
setChatMessages(snap.data().message)
readsRef.doc(snap.id).collection('read').doc(userID.uid).onSnapshot((snapshot1 => {
if (snapshot1.get('readReceipt') === userID.uid) {
setIsRead(true)
}
}))
})
})
return unsubscribe;
}, [isRead])
return (
<SidebarOptionChannel>
<span># </span>{title} - {chatMessages}<span>{isRead ? null : <UnreadBadge>
<span>1</span></UnreadBadge> }</span>
</SidebarOptionChannel>
)
Another solution to your problem could be to update your chat application by listening for changes in your Database of Firebase. This can be done by using the methods of firebase.database.Reference:
on() or once()
Here is an example provided by the documentation of Firebase:
var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', (snapshot) => {
const data = snapshot.val();
updateStarCount(postElement, data);
});
I am building a small online shopping web app and I have about 10 categories of products e.g. Mens, Womens, Kids etc. I want to memoize the requests. My question is how do I do it? I read about useCallback hook and I believe that I should use it here, but at the same time since there are like 10 categories how do I keep track for which category should I get from the cache and for which should I make the request? I have just considering declaring useState hooks for each category and change state for each depending on selected category.
const [productsMensCategory, setProductsMensCategory] = useState([]);
const [productsWomensCategory, setProductsWomensCategory] = useState([]);
const [productsKidsCategory, setProductsKidsCategory] = useState([]);
I have also been thinking that Redux might be useful here.
Could anyone please guide me a bit here, I would like to know how to approach this.
This is how I make a request.
const [products, setProducts] = useState([]);
useEffect(() => {
const getProducts = async (category: string) => {
try {
const response = await getCategoryProducts(category);
console.log(response);
if (response.status === 200) {
setProducts(response.data);
}
} catch (e) {
console.log(e);
}
};
getProducts(handleChangeCategory(section));
}, [section, setProducts]);
If you want these to stay saved even if you reload page you could use localStorage to make it easy. Redux would be a nice approach to not make another request, but as far as you reload it won't save state.
UseCallback only memorizes a callback, so if re-render happens this callback remains the same, it only changes if a dependency from its dependencies array changes
Good idea to have the data memoized. In my opinion, if at all you can get the data with flags based on date_modified for each category (as server response) you can then set or not set component state.
Something like below
if (response.status === 200 && category_flag_from_server !== current_category_flag_from_state) {
setProducts(response.data);
}
Ultimate goal is to store the JSON data. That way, if the same github user is sent to the GitHubUser component, instead of making a fresh call to the API, it should load the details from the local storage, preventing a network call.
Key Points about the problem.
do a simple fetch from github public api (no issues, working fine)
store the data to local storage with the github username as key (not working)
retrieve the data from local storage by providing a github username as key (not working)
display json data after render is complete using useEffect (working fine)
I get no errors of any kind with localStorage but nothing gets saved. I have tried this on both Firefox and Edge. The network call happens on every change of login, for the same user, which it should not.
Further, this code is from a textbook I am following, and this is a exact copy from the page that discusses fetch and useEffect. The author goes on to explain that it should work and so far the book has been correct with no errors.
I have put the code in a sandbox here - https://codesandbox.io/s/bold-http-8f2cs
Also, the specific code below.
import React, { useState, useEffect } from "react";
const loadJSON = key =>
key && JSON.parse(localStorage.getItem(key));
const saveJSON = (key, data) =>
localStorage.setItem(key, JSON.stringify(data));
function GitHubUser({ login }) {
const [data, setData] = useState(
loadJSON(`user:${login}`)
);
useEffect(() => {
if (!data) return;
if (data.login === login) return;
const { name, avatar_url, location } = data;
saveJSON(`user:${login}`, {
name,
login,
avatar_url,
location
});
}, [data]);
useEffect(() => {
if (!login) return;
if (data && data.login === login) return;
fetch(`https://api.github.com/users/${login}`)
.then(response => response.json())
.then(setData)
.catch(console.error);
}, [login]);
if (data)
return <pre>{JSON.stringify(data, null, 2)}</pre>;
return null;
}
//Jay-study-nildana
//MoonHighway
export default function App() {
return <GitHubUser login="Jay-study-nildana" />;
}
Note : I get a couple of warnings related to useEffect but I have already isolated that they are not the issue but I dont think they are the problem. it simple tells me not to use a dependency array since there is only one element for both useEffects. I am using the array on purpose.
Update 1
One thing I noticed is, in developer tools, nothing is getting stored in Local Storage after a successfull call to the API. So, right now, I am thinking, saving is not working. Unless I get that working and see the stored data in developer tools, I wont know if load is working or not.
First, if the initial state is the result of some computation, you may provide a function instead, which will be executed only on the initial render:
// instead of this
const [data, setData] = useState(
loadJSON(`user:${login}`)
);
// you better have this
const [data, setData] = useState(() => {
return loadJSON(`user:${login}`);
});
Second, you can achieve what you need with this single useEffect:
const [data, setData] = useState(() => { return loadJSON(`user:${login}`); });
useEffect(() => {
if (!data) {
fetch(`https://api.github.com/users/${login}`)
.then((response) => response.json())
.then((val) => {
saveJSON(`user:${login}`, val); // put data into localStorage
setData(val); // update React's component state
})
.catch(console.error);
}
});
if (data) return <pre>{JSON.stringify(data, null, 2)}</pre>;
return <div>no data</div>;
You will get your data in localStorage. Don't forget that you need to use key user:${login} if you need to get it from there.