Is it possible to reload on click on a Link component from next/link? I tried to put my own function on the child div from the link that says to reload on click. But then it reloads before the route has changed and it didn't change route anymore.
const router = useRoute();
const hanldeRefresh = () => {
router.reload();
}
<Link href={title.slug}>
<button onClick={handleRefresh} class="button">{title.buttonText}</div>
</Link>
Instead of using Link, you can use router like this
const router = useRoute();
const handleClick = async (title) => {
// Wait for route change before do anything
await router.push(title);
// Reload after routing
router.reload();
}
//<Link href={title.slug}>
<button onClick={() => handleClick(title.slug)} class="button">
{title.buttonText}
</button>
//</Link>
Related
I am developing a Certificate Management System where after all the processes have been done, the user may print a certificate.
I am struggling to implement such that upon clicking the print button, a new tab will open containing the ready to print HTML certificate so that the user will only CTRL + P to have the certificate printed.
How do i render my react certificate component in a new window? Such that i would only pass the props which are the data to be put into the certificate e.g., name, date etc.. like <Certificate name={john} />
I have tried implementing the npm react-new-window but it does not work with
<Button onclick={() => {
<NewWindow>
<CertificateComponent>
</NewWindow>
}}
>
PRINT BUTTON
</Button>
I have looked into react portals but most use cases are for Modals, which is where my "PRINT" button is rendered.
Sorry for the bad english/explanation. Thank you!
New Solution based on CreatePortal
import React, { useEffect, useCallback, useMemo, useState } from "react";
import { render, createPortal } from "react-dom";
const App = () => {
const [isOpen, setOpenState] = useState(false);
const open = useCallback(() => setOpenState(true));
const close = useCallback(() => setOpenState(false));
return (
<div>
<h1>Portals in React</h1>
<button onClick={open}>Open</button>
<button onClick={close}>Close</button>
{isOpen && (
<NewWindow close={close}>
Example <button onClick={close}>Close</button>
</NewWindow>
)}
</div>
);
};
const NewWindow = ({ children, close }) => {
const newWindow = useMemo(() =>
window.open(
"about:blank",
"newWin",
`width=400,height=300,left=${window.screen.availWidth / 2 -
200},top=${window.screen.availHeight / 2 - 150}`
)
);
newWindow.onbeforeunload = () => {
close();
};
useEffect(() => () => newWindow.close());
return createPortal(children, newWindow.document.body);
};
render(<App />, document.getElementById("root"));
There can be multiple approaches for this.
Approach 1:
Create a new route and map the certificateComponent to it, make sure it doesn't have any authentication or any dependency to it.
Store the required data for certificateComponent either in session storage or local storage.
When the user clicks on print button, redirect the user to this new route using window.open("http://localhost:port/newroute").
In certificateComponent read the values stored in session/local storage and map it accordingly.
Approach 2:
Make the certificate component as an overlay which occupies the entire screen which shows up when the user click on print button.
If any UI elements need to be hidden, you can do something as shown below:
printFn = function() {
// show the certificate component
// hide the ui elements not required
// window.print()
}
This worked for me
const myWindow: any = window.open('','', 'height: 500;width:500');
ReactDOM.render(<Yourcomponent prop={propValue} /> , myWindow.document.body);
myWindow.document.close();
myWindow.focus();
myWindow.print();
myWindow.close();
I am trying to show a message when user try to leave current page, so I am using history.block like this:
import { useHistory } from "react-router-dom";
const ProfilerCreate = ({ pageType }) => {
const history = useHistory();
const [isDisabled, setIsDisabled] = useState(true);
const [openModalUnsave, setOpenModalUnsave] = useState(false);
useEffect(() => {
history.block(validateChange);
}, []
);
//Function to validate changes and open modal
function validateChange(txt) {
if (!isDisabled) {
toggleModalUnsave();
return false;
}
}
//Function to open or close modal
function toggleModalUnsave() {
setOpenModalUnsave(!openModalUnsave);
}
//Function to return landing page
function returnPage() {
history.push("/");
}
return (
...
<div style={{ display: "none" }}>
<Modal
id="myModal"
heading="You have unsaved changes"
description="Do you want to save or discard them?"
isOpen={openModalUnsave}
onRequestClose={(detail) => toggleModalUnsave()}
actionsRight={
<>
<Button display="text" onClick={() => returnPage()}>
Discard
</Button>
<Button
display="primary"
onClick={(evt) => saveAudienceData(evt)}
>
Save and exit
</Button>
</>
}
>
<p>Modal Children</p>
</Modal>
</div>
);
export default ProfilerCreate;
when it is detecting unsaved changes, it shows a modal with a warning and two buttons, one for save and the other for discard, when the user hit discard button it should return to home page, but history.push is not working.
I tried to find the solution or I don't know if I am using the history.block in a wrong way.
I hope that you can help me, thanks!
I think you are missing the unblock() method in validateChange(txt)
I am having a Next JS app where there are very simple two pages.
-> Home page
import Header from "../components/header";
const handleForm = () => {
console.log("trigger");
};
export default () => (
<>
<Header />
<h1>Home</h1>
<form onSubmit={handleForm}>
<input type="text" placeholder="Username" />
<input type="password" placeholder="Password" />
<button type="submit"> Login </button>
</form>
</>
);
-> About page
import Header from "../components/header";
export default () => (
<>
<Header />
<h1>About us</h1>
</>
);
Requirement:
-> Home page has a login form
-> If user started typing in any of the fields then without submitting the form, if he tries to move to About us page then a warning needs to be displayed something similar like beforeunload_event.
I am not sure how we can handle it in react as I am new to it.. Kindly please help me to handle a alert if user trying to navigate to other url while editing the form fields..
From my understanding, you can achieve your goal by listen the event routeChangeStart as then throws exception in case of rejecting to move the target url.
I forked above codesandbox and created a simple demo based on your idea which doesn't allow to switch page in case of username having value (form is dirty).
Here is the general idea:
import router from "next/router";
export default () => {
// Assume this value holds the status of your form
const [dirty, setDirty] = React.useState();
// We need to ref to it then we can access to it properly in callback properly
const ref = React.useRef(dirty);
ref.current = dirty;
React.useEffect(() => {
// We listen to this event to determine whether to redirect or not
router.events.on("routeChangeStart", handleRouteChange);
return () => {
router.events.off("routeChangeStart", handleRouteChange);
};
}, []);
const handleRouteChange = (url) => {
console.log("App is changing to: ", url, ref.current);
// In this case we don't allow to go target path like this
// we can show modal to tell user here as well
if (ref.current) {
throw Error("stop redirect since form is dirty");
}
};
return (
// ...
)
}
The link codesandbox is here https://codesandbox.io/s/react-spring-nextjs-routes-forked-sq7uj
I am working on a REACT based web-app POC in my org. There is table of issues and for each of these issues, I have to provide a button in the table which when a user clicks on - it will open up a modal, fetch data for that issue via an API call and then broadcast the data in that modal.
The problem:
Let's say I have 300 issues listed in that table, hence there are 300 clickable buttons for opening modals and calling API. Now the problem is that, whenever that table loads, it calls APIs for all 300 issues at once, but I want each API to only be called when an user clicks on the respective button!
Here is the code for Modal component which I have managed so far:
import React, { FunctionComponent, useState, useEffect } from 'react'; // importing FunctionComponent
import { Modal, Button } from 'react-bootstrap';
type IssueReportProps = {
issueInfo: any
}
const IssueReport: FunctionComponent<IssueReportProps> = ({ issueInfo }) => {
const issueNumber: string = issueInfo.number;
const [show, setShow] = useState(false);
const [diagnosisInfo, setdiagnosisInfo] = useState({});
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
useEffect(() => {
async function fetchData() {
const res = await fetch("http://api-call/?issuenumber=".concat(issueNumber));
res.json().then(res => setdiagnosisInfo(res));
}
fetchData();
}, [issueNumber]);
console.log(diagnosisInfo);
return (
<>
<Button variant="outline-primary" onClick={handleShow} size="sm">
Diagnosis
</Button>
<Modal show={show} onHide={handleClose} backdrop="static" keyboard={false}>
<Modal.Body>
<p>
Issue Info: {JSON.stringify(diagnosisInfo)}
</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</>
);
};
export default IssueReport;
The console.log(diagnosisInfo); confirms my suspicions that once the issue is loaded, APIs for all issues are called. How to prevent this?
Please let me know if I need to provide more details.
EDIT1: Accepted Solution-
Here is the change I made to the code post #Dykotomee's solution:
// useEffect:
useEffect(() => {
async function fetchData() {
const res = await fetch("http://api-call/?issuenumber=".concat(issueNumber));
res.json().then(res => setdiagnosisInfo(res));
}
// fetchData();
if (show){
fetchData();
}
}, [issueNumber, show]);
useEffect is called every time the component renders. Therefore, when the table loads with 300 components, the API is fetched 300 times!
You only want to fetch if the modal is showing. Try wrapping your call to fetchData in a conditional:
if (show) {
fetchData();
}
It's not ideal, considering the modal will likely show prior to the fetch being completed, but you can adjust your code or add more states to compensate.
Simple question.
When I'm in a for instance /dashboard router and I click on <Link to="/users/:userID" > router and try to go back to /dashboard it works wine , but when from /users/:userID router I navigate to another /users/:userID router and try to go back I need to click the back button twice , any idea why?
e.g.
/dashboard -> /users/1 and back ( 1 click needed )
/dashboard -> /users/1 - > /users/2 and back to /users/1 ( 2 clicks
needed )
Here is my Route in App.js
<Route path='/users/:userId' render={()=><User/>} />
Here is my User.jsx render()
render() {
let movie = this.props.thisUserIdData;
const { match } = this.props;
console.log(match);
return (
<div> .... </div>
)
}
and the componentDidMount()
componentDidMount() {
this.loadData(this.props.match.params.userId);
//using redux and axios to get data
}
I had similar situation with nested onClick action.
For example:
<div onClick={() => push(`/user/${id}`)}>
<button onClick={() => push(`/user/${id}`)}>to user</button>
</div>