I working with react-pdf to display pdf using a link. I have completed the pdf viewer. What I want to do now is to make a pdf page counter. So that user know what page he is in.
What I have tried is to give window a scroll event which will increment the pages on specific number of scrolls. But its not working. Also I want user to enter page number in the space to navigate to that specific page.
This is how the page counter looks like. Please help.
use react-pdf module
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf';
function App() {
const [numPages, setNumPages] = useState(0);
const [pageNumber, setPageNumber] = useState(1);
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
}
return (
<div>
<p>{pageNumber} / {numPages}</p>
<Document
file="somefile.pdf"
onLoadSuccess={onDocumentLoadSuccess}>
<Page pageNumber={pageNumber} />
</Document>
</div>
);
}
export default App;
You can display page number and total number of pages like this
<Text render={({ pageNumber, totalPages }) => (
`${pageNumber} / ${totalPages}`
)} fixed />
se docs https://react-pdf.org/advanced
Related
I'm make a breadcrumb using reactjs and react-router v5. I'm getting error when i'm click to previous page in breadcumbs. the application becomes hang. here's my breadcrumb.
showing the recent url pages is work. But the problem is when i want to back to previous page from breadcrumbs, my apps become hang, and i'm getting this error.
And then i'm getting the weird things about url. when i'm done in the last page. when i'm hover the link in breadcrumbs , it looks same.
it's weird , because the url of dashboard is '/'.
Here's my breadcrumbs code.
import React from "react";
import { Breadcrumb } from "react-bootstrap";
import { Link, withRouter } from "react-router-dom";
const Breadcrumbs = (props) => {
const {
history,
location: {pathname}
} = props;
const pathnames = pathname.split("/").filter(x => x);
return (
<Breadcrumb aria-label="breadcrumb">
<Link onClick={() => history.push("/")}> Dashboard</Link>
{
pathnames.map((name, index) => {
const routeTo = `/${pathnames.slice(0, index + 1).join("/")}`;
const isLast = index === pathnames.length - 1;
return isLast ? (<>
<Breadcrumb.Item key={name}>{name}</Breadcrumb.Item> </>
) : (
<Link key={name} onClick={() => history.push(routeTo)}>{name}</Link>
)
})
}
</Breadcrumb>
);
};
export default withRouter(Breadcrumbs);
I want my breadcrumbs work like this.
Demo Breadcrumbs
i'm stuck. any help will be appreciated. Thank you.
The react-router-dom Link component requires a to prop with a valid string or Path object with pathname property.
Instead of
<Link onClick={() => history.push("/")}>Dashboard</Link>
...
<Link key={name} onClick={() => history.push(routeTo)}>{name}</Link>
use
<Link to="/">Dashboard</Link>
...
<Link key={name} to={routeTo}>{name}</Link>
I have a react page builder, I am displaying user-created pages using dangerous HTML now I want when the user clicks a button preview to open this page on a new window.
Here is my component to display user pages
import React, { useState, useEffect} from "react";
import api from "../utils/api";
function MyPages() {
const [MyPagesList, setMyPagesList] = useState();
const getMyPages = async () => {
try {
const response = await api.get("/pages");
setMyPagesList(response.data.data);
} catch (error) {}
};
useEffect(() => {
getMyPages();
}, []);
return (
<div className="all-mypages">
<div className="all-mypages__cards">
{MyPagesList && MyPagesList.map(function (data, id) {
return (
<div className="mypages-card" key={id}>
<a className="mypages-card__link">
<div className="mypages-card__content"
dangerouslySetInnerHTML={{__html: data.attributes.html}}></div>
</a>
<button className="preview " onClick={()=>history.push(`/mypages/preview/${data.attributes.html}`)>Preview</button>
</div>
);
})}
</div>
</div>
);
}
export default MyPages;
When user clicks preview button here is what I get
My solution is not working and I am out of ideas, what do I need to do to solve this issue?
The result is correct, because you are appending the html to /mypages/preview/. If you want the data.attributes.html ( which is the user html content ) to be displayed on a new page then:
Create another React component which looks for this html content from localStorage redux
When clicked on the preview button open a new page with url in react router pointing to this component
This component on load will get the localStorage html content and pass it to its dangerouslySetInnerHtml and display it.
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 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'm trying to use the createContext and useContext features of ReactJS to create a notification icon that displays a number of notifications increasing by 1 each second (essentially trying to figure out how to pass a state between files/components), but am unable to get the value to transfer over.
The timer appears to be working fine and the seconds variable in the timer file is definitely increasing, it's just the secs in the NavBar that isn't updating correctly.
--- App File ---
function App() {
return (
<div>
<NavBar />
<Timer />
</div>
);
}
export default App;
---Timer File---
...
export const secContext = createContext()
const Timer = () => {
const [seconds, setSeconds] = useState(0);
...
useEffect(() => {
...
if (isActive) {
interval = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);}
...
return (
...
<secContext.Provider value={seconds}>
<div className="time">
{seconds}s
</div>
...
</secContext.Provider>
...
);
};
---Navigation Bar File---
...
import {secContext} from './Timer'
export default function NavBar() {
const secs = useContext(secContext)
return (
...
<Badge badgeContent={secs}>
<NotificationsIcon />
</Badge>
...
);
}
I was expecting {secs} in the Navigation Bar file to update to be equal the value of the seconds value from the Timer, but instead it appears to remain null.
Here is a screenshot of the interface: https://gyazo.com/d283360091c9d4ea8d9b2785419ad665
In this case the notification icon should have a badge with the number 10.
Context values are only accessible if they are rendered within the hierarchy i.e.
<secContext.Provider value={x}>
<NavBar />
</secContext.Provider>
NavBar doesn't appear to be a descendant of your context, your context is created inside Timer and therefore is only accessible from descendants of that component e.g.
<Timer>
<NavBar />
</Timer>
Update
As per the comments discussion, if you simply want to share data between a couple of siblings, it would probably make more sense to just move the common up to the parent (App) and then pass the data into the siblings as props - see example.