How to remove delay on useMutation? - reactjs

I have a button and I want to using the loading prop from the useMutation hook to activate the spinner, but there is the slightest lag after i click the button due to the delay in response. The loading prop is not immediately set to true.
const SomeComponent = () => {
const [addTodo, { data }] = useMutation(ADD_TODO);
return (
...
<Button onClick={() => addTodo("test")} loading={loading}>Submit</Button>
// Will take 500ms or so until the spinner spins due to delay in receiving
// which defeats the purpose of the loading spinner
</form>
)}
Is there an elegant solution for ensuring loading is set to true the instant I fire the mutation?

Related

react-headroom - momentarily disable when animating scroll from another component

I have have a tabs-component that becomes sticky when a user scrolls past it's scroll position on the page. When a tab is clicked it will scroll the user up or down, depending on where their current scroll position is, in relation to the related tab-content's scroll position.
Is it possible to momentarily disable/reactivate the react-headroom functionality from another component, when required?
Ideally, when scroll-up is initiated via these tabs, I wish to trigger the react-headroom hide-header functionality, if the header is already shown, or disable the show-header functionality, if the header is already hidden. Any suggestions how one would achieve this?
Thanks in advance.
I ended up resolving this by storing a boolean value of false in useState, to control the toggling of the react-headroom's disable prop. As the scrollIntoView animation is based on time, not distance travelled, so you can rely 😅 on setting a setTimeout of 500ms to reset the the useRef value back to its original state.
When I want to trigger a scroll and/or disable the header hide/show functionality, I place a useRef on the scroll-trigger, which an onClick event calls a function. Within this function, I set useState to true (to activate the disable prop & disable the header), animate the page, then use a setTimeout of 500ms to reset the useRef value back it original state, which will re-activate the header functionality.
import { useRef, useState } from 'react';
import Headroom from 'react-headroom';
const SiteHeader = (): JSX.Element => {
const [headroomDisabled, setHeadroomDisabled] = useState(false);
const myRef = useRef<HTMLDivElement>(null);
const myRef2 = useRef<HTMLDivElement>(null);
const executeScroll = () => myRef.current?.scrollIntoView();
const executeScrollUp = () => {
setHeadroomDisabled(true);
myRef2.current?.scrollIntoView();
setTimeout(() => {
setHeadroomDisabled(true);
}, 500);
};
return (
<>
<Headroom disable={headroomDisabled}>
<h2>Test header content</h2>
</Headroom>
<div ref={myRef2}>Element to scroll to</div>
<button onClick={executeScroll}> Click to scroll </button>
<div style={{ marginTop: '150vh' }}></div>
<div ref={myRef}>Element to scroll to</div>
<button onClick={executeScrollUp}> Click to scroll </button>
</>
);
};
export default SiteHeader;
I ended up taking this a step further and placing this logic in some global state, as so any component could utilise this functionality 🍻

How to listen for time change in React?

I have a button which I want to remain disabled unless in a certain time window. Is there any way to do this in react?
I dont want to hard code it like
<button disabled={isCorrectTime()}>...
because if the user is already on the page, and the time changes to the correct time, the button will not get updated right? does anyone know of any solutions?
You call setTimeout inside of the useEffect Hook . useEffect method runs when it first renders then setTimeout block runs after some seconds (these seconds passed into the second parameter of the setTimeout method) then you call the clearTimeout() to cancel a timer .
Example :
import React, {useEffect, useState} from 'react'
function App() {
const [disabled, setdisabled] = useState(true)
useEffect(() => {
const timer = setTimeout(() => {
setdisabled(false);
}, 3000);
return () => clearTimeout(timer);
}, []);
return (
<div>
<h1> hello </h1>
<button disabled= {disabled} > click me </button>
</div>
)
}
export default App
Is the time window a specific amount of time after a certain event? If so, you can use a timeout function to change a state variable that you can then use to control the disabled state of the button:
const [isDisabled, setIsDisabled] = useState(true)
setTimeout(() => {
setIsDisabled(false);
}, 1000)
// set this time to whatever the desired length of time is
...
<button disabled={isDisabled} />
If this doesn't work, you might be able to use setInterval() to periodically check the current time, but I will need more information about what your goals are to know if that's the best way forward.

React Link To button timeout

I have a simple button that when I click him he redirect me to another page in the website
now using react router ( <Link to=''>). now what's I want is when I click the button he wait 3 seconds and then redirect me
how can I accomplish this?
Thanks
You could create a custom link component that takes an additional delay.
Example:
Use a click handler to prevent the default link action so you can "inject" your delayed navigation. Use a React ref to store a reference to the timer and a mounting useEffect hook to clear any running timers in the case the component is unmounted prior to expiration. Use the useNavigate hook to then imperatively navigate with all the appropriate parameters.
import { Link } from 'react-router-dom';
const DelayedLink = ({ delay, replace, state, to, ...props }) => {
const navigate = useNavigate();
const timerRef = useRef();
useEffect(() => () => clearTimeout(timerRef.current), []);
const clickHandler = (e) => {
e.preventDefault();
timerRef.current = setTimeout(navigate, delay, to, { replace, state });
};
return <Link to={to} {...props} onClick={clickHandler} />;
};
Usage:
<DelayedLink delay={3000} to="/test">
Test
</DelayedLink>
<DelayedLink delay={3000} to="/test" state="foobar">
Test w/state
</DelayedLink>
<DelayedLink to="/test">Test w/o delay</DelayedLink>
Sounds like you want to use setTimeout. If you wrap setTimeout around your button's onClick method then you will be able to introduce a delay.
For example, here's a button which when you click it it waits 3 seconds (3000 milliseconds) before opening Stack Overflow in another tab:
<button
onClick={() => setTimeout(() => window.open("https://stackoverflow.com/"), 3000)}
>
Click me!
</button>
<button
onClick={() => setTimeout(() => window.open("url", '_blank'), 3000)}
>
I will run after 3 seconds
</button>
This will work!
or try with the useNavigate in react-router-dom

React Native - Delay between changing bottom tabs

I have used react navigation's "createBottomTabNavigator" in one of my projects.
const Tab = createBottomTabNavigator();
</Tab.Navigator>
................
<Tab.Screen name="Cart" component={Cart} />
{configuration.paymentProviders.length > 0 && (
<Tab.Screen name="Payment" component={Payment} />
)}
</Tab.Navigator>
My problem is this:
If I switch between two tabs quickly, after a while, the render cannot work properly because it tries to perform the operation without getting a response from the API.
For this, I added setLoading(true) to the API request and did not set it to false until the request was finished, but I still have the same problem, although it is better.
So, I want to give a half-second pause between tabs, do you have any suggestions for this?
When you switch the tabs, you use somthing like onClick() right? Here is old question for the solution as setTimeout. It gives you the ability to delay before the code gets executed. But I advice you do disable all other buttons, before you get a response. This also means that you need to be sure that response will come, at least timeout, then you can enable buttons again.
A better option might be to consider not delaying the process but just simply disabling all buttons which have onClick functionality, to prevent too many requests. This is an example of disabling something, doesn't neccessarely needs to be a button.
export default function App() {
const [clicked, setClicked] = React.useState(false);
//fake request, just delaying things
function handleClick() {
setClicked(true); // disable all buttons if clicked
setTimeout(() => {
setClicked(false);
}, 2000);
}
useEffect(() => {
console.log(clicked);
}, [clicked]);
return (
<div className="App">
<button onClick={!clicked ? handleClick : undefined}>1</button>
<button onClick={!clicked ? handleClick : undefined}>2</button>
</div>
);
}

useEffect when onClick

I'm getting stuck about how to use effects together with app logic.
Suppose this component:
import React, { useEffect, useState } from "react";
export default function App() {
const [query, setQuery] = useState('');
useEffect( () => {
fetch('https://www.google.com?q='+query)
.then(response => console.log(response))
}); // depends on what?
return (
<div>
<input onChange={e => setQuery(e.target.value)} value={query} />
<button>Ask Google about {query}</button>
</div>
);
}
I want that:
when (and only) the user clicks the button the fetch is run with the correct query value of the input
if the fetch is still in progress and the user clicks, the fetch is skipped but the effect is fired (meaning: I intentionally not disable the button, I want that the effect function is run, but I put a check inside that function not to execute the fetch).
Problems:
The effect shouldn't fire on mount (it wouldn't make any sense)
The effect shouldn't fire when the query changes, but if I don't put the query variable inside the useEffect dependency array, React complains (react-hooks/exhaustive-deps)
The effect should fire when the user click on the button; I achieved this for example using a fake state isRun, setting onClick={setIsRun(true)}, making the effect depending on [isRun], setting setIsRun(false) at the end of the effect function, and checking if (!isRun) at the beginning of the effect function to prevent that when is set to false from the effect itself it is run again since the state changes. This works, but I find it very verbose and uncomfortable...
The effect should fire if the button is clicked again (with the same query value or not) and the previous fetch has not yet finished without running the fetch: with the previous solution with isRun it wouldn't fire because isRun is already set to 1 so there is no state change; maybe with another state there is a way, but again very verbose and counterintuitive.
Most importantly: the code should be clean and readable without using "tricks"!
How would you write such a component?
It sounds like you shouldn't be using useEffect for this at all. You want this to happen on a user action, not as an effect:
when (and only) the user clicks the button the fetch is run with the correct query value of the input
Remove useEffect and create a function to handle the click:
const handleClick = (e) => {
fetch('https://www.google.com?q='+e.target.value)
.then(response => console.log(response));
};
And pass that function to the component:
<button onClick={handleClick}>Ask Google about {query}</button>
What seems confusing here are these requirements:
if the fetch is still in progress and the user clicks, the fetch is skipped but the effect is fired
The effect should fire if the button is clicked again (with the same query value or not) and the previous fetch has not yet finished without running the fetch
The only thing the function does is execute a fetch. So should that operation happen or not? Your proposed solution of keeping state in a variable (isRun) to determine if it should happen or not should work in this case. I think the problem before was mixing that up with useEffect when all you really want is a function. Add isRun to state and update it accordingly when performing the operation:
const [isRun, setIsRun] = useState(false);
const handleClick = (e) => {
if (isRun) { return; }
setIsRun(true);
fetch('https://www.google.com?q='+e.target.value)
.then(response => {
console.log(response);
setIsRun(false);
});
};
I'm not sure if you want to insist on using useEffect but it does not seem appropriate for this situation. What I could do is call a function on button click.
import React, { useEffect, useState } from "react";
export default function App() {
const [query, setQuery] = useState('');
const handleQuery = (query) => {
fetch('https://www.google.com?q='+query)
.then(response => console.log(response))
}
return (
<div>
<input onChange={e => setQuery(e.target.value)} value={query} />
<button onClick={() => handleQuery(query)}>Ask Google about {query}</button>
</div>
);
}

Resources