React Link To button timeout - reactjs

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

Related

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.

Can I createPortal from an onClick handler?

Given something like this:
import { render, createPortal } from "react-dom"
const MyPage = () => {
const handler = () => {
render(<MyModalDialog />, document.body)
}
return (
<div>
<button onClick={handler}>Click me</button>
</div>
)
}
I can conditionally render my modal dialog directly onto the body (leaving cleanup to the dialog perhaps, but that's not important for this question). This works, but the problem is there is no Context available (i.e., useContext).
As of v16. there is an api for this case, createPortal. This keeps the context around and allows rendering onto an arbitrary DOM node. But, this has no effect inside a click handler as shown above - it is meant to be returned from the <MyPage/> component, and react handles the actual rendering elsewhere.
So my question is - can I conditionally render an arbitrary component to a specified DOM node as part of an event handler using createPortal or some other API?
A better method to do it would be having the portaled element in a separate component and having a state in your MyPage component that renders the modal conditionally
Something like
const MyPortaledModal = () => createPortal(<MyModalDialog />, document.body)
const MyPage = () => {
const [shouldShowModal, setShouldShowModal] = useState(false);
const toggleModal = () => setShowModal(prevValue => !prevValue)
return (
<div>
<button onClick={toggleModal}>Click me</button>
{shouldShowModal && <MyPortaledModal/>}
</div>
)
}

How to navigate between pages in react with hooks

I'm a beginner at react hooks and I am trying to assign a state to every page of my module and set the initial state to an intro page and change state (page) on clicking next button.
This is how my main payment page looks
const [portalStage, setPortalStage] = useState("INTRO");
const Screens = {
INTRO: <IntroScreen />,
Post_INTRO: <Payment />, }
useEffect(() => {
setScreen();
}, [context]);
const setScreen = async () => {
setPortalStage("Post_INTRO");
}
{Screens[portalStage]}
<IntroScreen onClick = {setScreen} />
Now I am trying to add an Intro page to it which should always open first and then on clicking the next button on the introscreen it should redirect to the main component.
const IntroScreen = () => {
return (
<Wrapper>
<Content>
<h1>Coupons only!</h1>
<p>Increase your eligibility limit today!</p>
<Button>
Next
</Button>
</Content>
</Wrapper>
);
};
export default IntroScreen;
With this approach I can only see the main page and not the introscreen. What am I doing wrong in assigning state to both screens
A clean way to do this if you dont want use different routes would be to re-render when the next button is clicked. Some thing like below:
MainPaymentPage.js
const MainPaymentPage =(props)=>{
const [isNextClicked,setNextClicked]=useState(false);
let componentCode=<IntroScreen click={()=>setNextClicked(true)}/>
if(isNextClicked){
componentCode=<Payment/>
}
return(
{componentCode}
)
}
export default MainPaymentPage;
and add a click listener in your IntroScreen component like below:
const IntroScreen = (props) => {
return (
<Wrapper>
<Content>
<h1>Coupons only!</h1>
<p>Increase your eligibility limit today!</p>
<Button onClick={props.click}>
Next
</Button>
</Content>
</Wrapper>
);
};
export default IntroScreen;
you will have to make a similar change in your button component so that it can handle the click event. If the Button component comes from a framework like MaterialUI or Bootstrap, it should be able to handle it on its own, but you might have to rename the listener from onClick to whatever your framework wants.
The way the above code works is that there is now a parent component which has a state deciding which component to display based on the state value(isNextClicked, in this case). Initially, it will be set to false, causing the componentCode variable to be set to the code for IntroScreen. When the next button is clicked in the intro screen, it will change the state of the parent component(MainPaymentPage, in this case) to true, causing a re-render. This time, since isNextClicked is true, componentCode will be set to the code for your Payment component.

Hooks parent unmounted before children

I' working on react since few months. I started Hooks since few days (I know quite late) the thing is, compare to react component the life cycle methodes it's look like they are different on some points.
The useEffect hook can reproduce :
-componentDidMount();
-componentDidUpdate();
-componentWillUnMount();
But I observe a difference between react's component and function it's about the way how function is unmounted. I noted the unmount methode, compare to the react's component,the react's function unmount the parent before the child/ren
import React, { ReactElement, useEffect, useState } from "react";
import { useLocation, useHistory } from "react-router-dom";
export function Child2({
count,
childrenUnmounted,
}: {
count: number;
childrenUnmounted: Function;
}): ReactElement {
useEffect(() => {
return () => {
console.log("Unmounted");
childrenUnmounted(count);
};
}, [, count]);
return (
<div>
<h2>Unmouted</h2>
</div>
);
}
export function Child1({ count }: { count: number }): ReactElement {
const [validation, setValidation] = useState(false);
const usehistory = useHistory();
const childrenUnmounted = (count: number) => {
console.log("validation", validation, count);
setValidation(false);
};
const changeUrl = () => {
setValidation(true);
usehistory.push("http://localhost:3000/${count}");
};
return (
<div>
<h2>incremente</h2>
<Child2
count={count}
childrenUnmounted={(count: number) => childrenUnmounted(count)}
/>
<button className="button" onClick={() => changeUrl()}>
validation
</button>
<button
className="button"
onClick={() => usehistory.push(`http://localhost:3000/${count}`)}
>
nope
</button>
</div>
);
}
export default function Parent(): ReactElement {
const [count, setcount] = useState(-1);
const location = useLocation();
useEffect(() => {
setcount(count + 1);
}, [, location]);
return (
<div>
<h2>hello</h2>
<h3>{count}</h3>
<Child1 count={count} />
</div>
);
}
With the code above something annoying happen, when you clicked on the validation button. Value in the Child1is at true, at the moment of the click, and it's change the URL to trigger a rerender of the Parent to change the data (here count).
The thing I don't understand is why at the unmount of the Child2, at the childrenUnmounted(count) called (to trigger the same function but in the Child1) in the Child1 the validation is equal to false even the validation was clicked ? and when you click on nope just after validation you got true... it's look like the Child1 do not matter of the current state of the validation (he use the previous state)
Someone could help me to understand what's going on ?
Thx of the help.
SOLUTION:
I used useRef instead of useState from the validation to don't depend of the re-render as Giovanni Esposito said :
because hooks are async and you could not get the last value setted for state
So useRef was my solution
Ciao, I think you problem is related on when you logs validation value. I explain better.
Your parent relationship are: Parent -> Child1 -> Child2. Ok.
Now you click validation button on Child2. validation button calls changeUrl that calls usehistory.push("http://localhost:3000/${count}"); and starts to change validation value (why starts? because setValidation is async).
If the unmounting of Child2 comes now, could be that validation value is no yet setted by async setValidation (and log returns the old value for validation).
Well, at some point this setValidation finished and sets validation to true. Now you click nope button and you get true for validation (the last value setted).
So, to make the story short, I think that what you are seeing in logs it's just because hooks are async and you could not get the last value setted for state (if you use log in this way). The only way you have to log always the last value setted is useEffect hook with value you want to log in deps list.

useEffect function open/close modal based in props works only at first time (click)

Im learning React and using the new implemented "Hooks" from Documentation. I got a problem with a Modal (Dialog from Material UI) and the open/close function using useEffect() function.
I have already read these both articles: React.useState does not reload state from props and How to sync props to state using React hook : setState()
It's already helped me, I have forgot to use the useEffect() function instead I was just setting the useState from what comes from props. Learned that useState will be executed only one time for setting the initial state. But I have however more one problem.
function AddItemModal(props) {
const [open, setOpen] = useState(props.isOpen);
useEffect(() => {
setOpen(props.isOpen);
}, [props.isOpen]);
function handleClose() {
setOpen(false);
}
That works for the first time when I click at the add button (in another Component) and handle the click (it changes the state to true) and I pass it as props to the Modal. But when I click (in modal) at close and try to click at add to open it again, nothing happens. In case needed, here's the code from the component where I handle the click and call the modal.
function ItemsTable() {
const [addModalOpen, setAddModalOpen] = React.useState(false);
const handleAddClick = () => {
setAddModalOpen(true);
};
<Box mt={4} position="fixed" bottom={10} right={10}>
<Fab color="secondary" aria-label="Add" onClick={handleAddClick}>
<AddIcon />
</Fab>
</Box>
<AddItemModal isOpen={addModalOpen} />
You have split your modal state across two components which is confusing and going to lead to bugs like this. You should put the state in one place and update it where necessary. In this case, you could keep the modal state in the ItemsTable and pass in a handler for the modal to access.
Something like:
function ItemsTable() {
const [addModalOpen, setAddModalOpen] = React.useState(false);
const handleAddClick = () => {
setAddModalOpen(true);
};
const handleClose = ()=>{
setAddModalOpen(false)
}
<Box mt={4} position="fixed" bottom={10} right={10}>
<Fab color="secondary" aria-label="Add" onClick={handleAddClick}>
<AddIcon />
</Fab>
</Box>
<AddItemModal isOpen={addModalOpen} handleClose={handleClose}/>
I am not sure if I understood exactly what are you trying to do, but I see that useEffect does not use the state. In order useEffect to be called more than one times, you need to pass the state to it, so your [props.isOpen] needs to change to [open]

Resources