Why is useQuery making my component re-render when I come back to the tab? - javascript

I'm using react-query library, and I looked it up, and useQuery can make your react component re-render because it has states, but the weird thing is that it re-renders when I change tabs (like, I go to youtube, then come back to my app and the component just re-renders). But it only happens with useQuery, and I don't understand why.
Edit: It also happens when I click my console and then click the app again
The code is very simple:
const { data } = useQuery("pokemon", () =>
axios("https://pokeapi.co/api/v2/pokemon/")
);
console.log(data);
It's literally happening right now. Every time I go back to my app, it just logs the data again. I don't know what I'm missing

If you're using the react-query library, consider setting the refetchOnWindowFocus option to false
So your code should look like this
const { data } = useQuery("pokemon", () =>
axios("https://pokeapi.co/api/v2/pokemon/"),
{
refetchOnWindowFocus: false
}
);
console.log(data);
You can also set a refetchInterval option in milliseconds to only refetch at certain time intervals

Related

How to handle browser forward and back button in react-router-dom v6

I do not want a user to click browser back button and forward button from my page because my page loads stored sessions every time. If user is clicking on these buttons then i simply want him to navigate to the homepage.
For security purpose, sessions are being maintained by the backend APIs so if user will fluctuate on browser's forward and back buttons then it will cause session disruption.
I have searched everywhere and tried lots of things but no solution is compatible with the v6 of react-router-dom. There must be solution for this. Please help me out in getting the same.
this was my question recently. I searched a lot but have nothing found to do this work with react-router v6. but you can handle browser back and forward buttons using window 'popstate' event. I wrote a hook for connecting, detect and handle this event(useBackButton):
import { useEffect, useState } from "react";
const useBackButton = (callback) => {
const [isBack, setIsBack] = useState(false);
const handleEvent = () => {
setIsBack(true);
callback();
window.history.go(1);
};
useEffect(() => {
window.addEventListener("popstate", handleEvent);
return () => window.removeEventListener("popstate", handleEvent);
});
return isBack;
};
export default useBackButton;
you can write your function that does your desired work and send it to this hook. then call this hook in every component that you want.

useEffect for real time data update

I am creating an easy chat app, with different text channels. I am facing an infinite loop issue when using the useEffect hook to update the messagesList on real time. You can see the code below but this is what I am trying to achieve:
First useEffect is for the chat window to scroll to the last message every time there is a change in the messagesList array. This means: I am in the middle of the messages window, I write a new message and it takes me to the bottom. This is working fine.
Second useEffect is for the messagesList to be rendered whenever the channel is changed or there is any change in the messagesList. Adding the messagesList as a dependency is causing the infinite loop... but I think I need it cause otherwise the following happens: user1 is inside the chat channel and user2 writes a new message. User1 wont see the new message displayed as his chat is not being re-rendered. How would you make it for the new message to be displayed for user1?
Sorry for the confusing question and thanks a lot in advance!
useEffect(() => {
anchor.current.scrollIntoView(false);
}, [messagesList]);
useEffect(() => {
const unsubscribe = onSnapshot(
collection(firestore, `channels/${activChannel}/messages`),
(snapshot) => {
console.log(snapshot.docs);
getMessagesList();
}
);
return () => unsubscribe();
}, [activChannel, messagesList]);
I am not familiar with firestore, but perhaps you could tie the updating of the messages to the event that an user submits his message or use useSyncExternalStore. This piece of documentation on useEffect use cases might help you.
an excerpt from the docs:
Here, the component subscribes to an external data store (in this
case, the browser navigator.onLine API). Since this API does not exist
on the server (so it can’t be used to generate the initial HTML),
initially the state is set to true. Whenever the value of that data
store changes in the browser, the component updates its state.
Although it’s common to use Effects for this, React has a
purpose-built Hook for subscribing to an external store that is
preferred instead. Delete the Effect and replace it with a call to
useSyncExternalStore:

how do i update a navbar on an api call (specifically a google signin)

i'm having a tough time understanding the hook concept in this case:
the user signs in through google, updating the session state.
this updates a visitorType state from 'viewer' to 'buyside'...
and then my navbar is supposed to adjust. but it doesn't, unless switch from the browser to another app and then back again...
const { data: session } = useSession(); //next-auth login sessions
const [visitorType, setVisitorType] = useState('viewer')
const visitorRef = useRef(visitorType);
useEffect(()=>{
return(
() => {if (session) {
visitorRef.current='buyside';
setVisitorType('buyside')}
}
)
},[session])
from what i understand, useRef gets the value of something between re-rerenders? and useEffect is asynchronous, so it lags behind? - so, not sure how i can update a reference..
The function returned by useEffect fires when the components unmounts. I think what you want is:
useEffect(()=>{
if (session) {
visitorRef.current='buyside';
setVisitorType('buyside')}
}
},[session])
Regarding your question on useRef:
when you set visitorRef.current. No rerender is triggered.
when you call setVisitorType, a rerender happens

how to emulate messages/events with react useState and useContext?

I'm creating a react app with useState and useContext for state management. So far this worked like a charm, but now I've come across a feature that needs something like an event:
Let's say there is a ContentPage which renders a lot of content pieces. The user can scroll through this and read the content.
And there's also a BookmarkPage. Clicking on a bookmark opens the ContentPage and scrolls to the corresponding piece of content.
This scrolling to content is a one-time action. Ideally, I would like to have an event listener in my ContentPage that consumes ScrollTo(item) events. But react pretty much prevents all use of events. DOM events can't be caught in the virtual dom and it's not possible to create custom synthetic events.
Also, the command "open up content piece XYZ" can come from many parts in the component tree (the example doesn't completely fit what I'm trying to implement). An event that just bubbles up the tree wouldn't solve the problem.
So I guess the react way is to somehow represent this event with the app state?
I have a workaround solution but it's hacky and has a problem (which is why I'm posting this question):
export interface MessageQueue{
messages: number[],
push:(num: number)=>void,
pop:()=>number
}
const defaultMessageQueue{
messages:[],
push: (num:number) => {throw new Error("don't use default");},
pop: () => {throw new Error("don't use default");}
}
export const MessageQueueContext = React.createContext<MessageQueue>(defaultMessageQueue);
In the component I'm providing this with:
const [messages, setmessages] = useState<number[]>([]);
//...
<MessageQueueContext.Provider value={{
messages: messages,
push:(num:number)=>{
setmessages([...messages, num]);
},
pop:()=>{
if(messages.length==0)return;
const message = messages[-1];
setmessages([...messages.slice(0, -1)]);
return message;
}
}}>
Now any component that needs to send or receive messages can use the Context.
Pushing a message works as expected. The Context changes and all components that use it re-render.
But popping a message also changes the context and also causes a re-render. This second re-render is wasted since there is no reason to do it.
Is there a clean way to implement actions/messages/events in a codebase that does state management with useState and useContext?
Since you're using routing in Ionic's router (React-Router), and you navigate between two pages, you can use the URL to pass params to the page:
Define the route to have an optional path param. Something like content-page/:section?
In the ContentPage, get the param (section) using React Router's useParams. Create a useEffect with section as the only changing dependency only. On first render (or if section changes) the scroll code would be called.
const { section } = useParams();
useEffect(() => {
// the code to jump to the section
}, [section]);
I am not sure why can't you use document.dispatchEvent(new CustomEvent()) with an associated eventListener.
Also if it's a matter of scrolling you can scrollIntoView using refs

Using React and Redux Hooks, why is my action not firing?

Edit: SOLVED! Please see below.
I want my Blog component to fire the fetchBlog action creator every time the browser requests its URL, be it via a link or a refresh. I'd like to do it with the React useEffect Hook and with the React-Redux useDispatch and useSelector Hooks. However, my action only fires when following the link to the page; I do not understand why, even after reading several explanations (like the official docs).
Here is the code:
// Everything duly imported, or else VSC would yell at me
export default function Blog() {
const dispatch = useDispatch();
// slug is set here with useSelector, this always works
useEffect(() => {
dispatch(fetchBlog(slug))
}, [slug, dispatch]);
const blog = useSelector((state) => state.blogs[0]);
// return renders the blog information from the blog constant
// since the action does not fire, blog is undefined because state.blogs is an empty array
}
I know that, on refresh, fetchBlog does not fire because of Redux DevTools and also because I put a debugger there. (And the back-end logs don't show the request coming in.) The action creator itself and the reducer must be working; if they weren't, the page would not load correctly when visited through a link.
Edit: I have determined useSelector and useDispatch are not the root cause of the problem, as changing the code to use connect with mapStateToProps and mapDispatchToProps gives the same result. The issue seems to be with useEffect.
I think the problem is you are returning the call to dispatch. Functions returned from useEffect are clean up functions, so I don't think this would run on mount, or update - only before unmount. Try this:
export default function Blog() {
// ...
// Don't return from useEffect. Just call dispatch within the body.
useEffect(() => {
dispatch(fetchBlog(slug);
}, [slug, dispatch]);
// ...
}
https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect
I'd like to clarify what the issue was, which #Trace guided me to finding.
useEffect wasn't being called on refresh because it gets called after the component renders/returns. When refreshing, the state - including the blog data - is lost; instead of returning, a TypeError is thrown because data.title doesn't exist. So useEffect never gets the chance of being called and fetch the blog's content.
The solution to that goes like this:
export default function Blog() {
// ...
useEffect(/* ... */)
const blog = useSelector((state) => state.blogs[0]);
if (!blog) {
return <p>Loading...</p>
}
// return actual blog contents here
}
So now fetchBlog does get called, updating blog and rendering the content.
It isn't clear to me where the slug comes from.
In theory useEffect runs after every render. In case of multiple parameters it will run the callback when one of the array parameters passed in the second argument changes.
Either create a useEffect with empty array as second argument to run it 'once' (e.g. when you refresh) or check the slug value.
Edits after checking the repo:
It's not going to work because useEffect is run AFTER the render (which was included in my answer although someone downvoted it). Dispatching the call will only happen after, or not at all if the exception was thrown before (in this case a nullpointer).
You can get the slug from react-router with match, may be handy for you to know.
export default function Blog({ match }) {
const slug = match.params.slug;
etc
The git repo shows how dispatch as is added as array parameter to useEffect, which is not necessary.

Categories