react-pdf won't render basic pdf - react-pdf

Trying to render a pdf using react-pdf and I swear I've done every configuration of their docs and nothings working. I keep getting
ReferenceError: $RefreshReg$ is not defined
Proof of concept component. Yes, the options aren't be used in this snippet but I've tried using it. I tried bringing in the cmaps directly into this components and referencing it there. I tried the copy webpack plugin and doing exactly as the documentation suggest. Nothing has worked. It's always this same error. Hoping I've just missed some minor detail.
/* eslint-disable #typescript-eslint/no-var-requires */
/* eslint-disable #typescript-eslint/no-unsafe-assignment */
/* eslint-disable #typescript-eslint/no-empty-function */
/* eslint-disable #typescript-eslint/ban-ts-comment */
import { Container, Paper } from '#material-ui/core';
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack';
import { pdfjs } from 'react-pdf';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
pdfjs.GlobalWorkerOptions.workerSrc = 'pdf.worker.min.js';
const options = {
cMapUrl: `//cdn.jsdelivr.net/npm/pdfjs-dist#${pdfjs.version}/cmaps/`,
cMapPacked: false
};
export default function TVRPreview() {
const [numPages, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
function onDocumentLoadSuccess({ numPages }: any) {
setNumPages(numPages);
}
return (
<Container>
<Paper>TVR Preview</Paper>
<Paper elevation={6}>
<Document file="./PDFTestTVR.pdf" onLoadSuccess={onDocumentLoadSuccess}>
<Page pageNumber={pageNumber}></Page>
</Document>
</Paper>
</Container>
);
}
Project Details
Create React App using react-app-rewired
"start": "react-app-rewired start",

Just to be sure is everything working as well, make the following steps:
Change the const options to:
const options = {
cMapUrl: 'cmaps/',
cMapPacked: true,
};
and here:
<Document file="./PDFTestTVR.pdf" onLoadSuccess={onDocumentLoadSuccess}>
Change this file reference to any pdf located someplace on the internet.

Two problems are possible here..
Make sure your local pdf file is located in public folder.
Use pdfjs.version like this instead of passing in options.
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
Documentation: react-pdf

Related

Next js and Next Auth overlapping react declarations

I am running Next js and Next Auth in multiple project, and all of a sudden all of them decided to crash with the same error.
Module parse failed: Identifier '_react' has already been declared (14:6)
File was processed with these loaders:
* ./node_modules/next/dist/build/webpack/loaders/next-swc-loader.js
You may need an additional loader to handle the result of these loaders.
| const _material = require("#mui/material");
| const _xDataGrid = require("#mui/x-data-grid");
> const _react = require("next-auth/react");
| const _reportTable = /*#__PURE__*/
a simple example that crashes looks like this...
As you can see from the example below. I am not importing react twice.
import React from "react";
import { Box } from "#mui/material";
import { DataGrid, GridColDef, GridRowsProp } from "#mui/x-data-grid";
import { getSession } from "next-auth/react";
import ReportTable from "../src/components/ReportTable";
export default function Home() {
const findSession = () => {
const session = getSession();
console.log(session);
return session;
};
return (
<Box>
<ReportTable title="Price Books">
<DataGrid
sx={{ border: "0" }}
rows={rows}
columns={columns}
headerHeight={40}
/>
</ReportTable>
</Box>
);
}
If I remove the getSession import at the top everything runs fine. The other developers on my team can run these project just fine, so I believe it's an environmental issue on my side.
Has anyone else run into this issue?
I have built the project and it works fine. The errors only occur in my dev environment.
I also cloned the repo on my personal machine and it worked fine there as well.
The problem was with a new plugin that came out today, "Code Ninja". If you are facing this issue, disable that VSCode extension.

Meteor and withTracker: why is a component rendered twice?

I have created a bare-bones Meteor app, using React. It uses the three files shown below (and no others) in a folder called client. In the Console, the App prints out:
withTracker
rendering
withTracker
rendering
props {} {}
state null null
In other words, the App component is rendered twice. The last two lines of output indicate that neither this.props nor this.state changed between renders.
index.html
<body>
<div id="react-target"></div>
</body>
main.jsx
import React from 'react'
import { render } from 'react-dom'
import App from './App.jsx'
Meteor.startup(() => {
render(<App/>, document.getElementById('react-target'));
})
App.jsx
import React from 'react'
import { withTracker } from 'meteor/react-meteor-data'
class App extends React.Component {
render() {
console.log("rendering")
return "Rendered"
}
componentDidUpdate(prevProps, prevState) {
console.log("props", prevProps, this.props)
console.log("state", prevState, this.state)
}
}
export default withTracker(() => {
console.log("withTracker")
})(App)
If I change App.jsx to the following (removing the withTracker wrapper), then the App prints only rendering to the Console, and it only does this once.
import React from 'react'
import { withTracker } from 'meteor/react-meteor-data'
export default class App extends React.Component {
render() {
console.log("rendering")
return "Rendered"
}
componentDidUpdate(prevProps, prevState) {
console.log(prevProps, this.props)
console.log(prevState, this.state)
}
}
What is withTracker doing that triggers this second render? Since I cannot prevent it from occurring, can I be sure that any component that uses withTracker will always render twice?
Context: In my real project, I use withTracker to read data from a MongoDB collection, but I want my component to reveal that data only after a props change triggers the component to rerender. I thought that it would be enough to set a flag after the first render, but it seems that I need to do something more complex.
This a "feature", and it's not restricted to Meteor. It's a feature of asynchronous javascript. Data coming from the database arrives after a delay, no matter how quick your server is.
Your page will render immediately, and then again when the data arrives. Your code needs to allow for that.
One way to achieve this is to use an intermediate component (which can display "Loading" until the data arrives). Let's say that you have a component called List, which is going to display your data from a mongo collection called MyThings
const Loading = (props) => {
if (props.loading) return <div>Loading...</div>
return <List {...props}></List>
}
export default withTracker((props) => {
const subsHandle = Meteor.subscribe('all.myThings')
return {
items: MyThings.find({}).fetch(),
loading: !subsHandle.ready(),
}
})(Loading)
It also means that your List component will only ever be rendered with data, so it can use the props for the initial state, and you can set the PropTypes to be isRequired
I hope that helps
Unsure if you're running into the same error I discovered, or if this is just standard React behavior that you're coming into here as suggested by other answers, but:
When running an older (0.2.x) version of react-meteor-data on the 2.0 Meteor, I was seeing two sets of distinct renders, one of which was missing crucial props and causing issues with server publications due to the missing data. Consider the following:
// ./main.js
const withSomethingCount = (C) => (props) => <C { ...props } count={ ... } />
const withPagination = (C) => (props) => <C { ...props } pagination={ ... } />
const withSomething = withTracker((props) => {
console.log('withSomething:', props);
});
// Assume we're rending a "Hello, World" component here.
export const SomeComponent = withSomethingCount(withPagination(withSomething(...)));
// Console
withSomething: { count: 0 }
withSomething: { count: 0, pagination: { ... } }
withSomething: { count: 0 }
withSomething: { count: 0, pagination: { ... } }
For whatever reason, I was seeing not only N render calls but I was seeing N render calls that were missing properties in a duplicate manner. For those reading this and wonder, there was one and only one use of the component, one and only one use of the withTracker HoC, the parent HoCs had no logic that would cause conditional passing of props.
Unfortunately, I have not discovered a root-cause of the bug. However, creating a fresh Meteor application and moving the code over was the only solution which removed the bug. An in-place update of the Meteor application (2.0 to 2.1) and dependencies DID NOT solve the issue... however a fresh installation and running a git mv client imports server did solve my problems.
I've regrettably had to chalk this up to some form of drift due to subsequent Meteor updates over the two years of development.

Dynamically load .css based on condition in reactJS application

I have a reactJS application that I want to make available to multiple clients. Each clients has unique color schemes. I need to be able to import the .css file that corresponds to the specific client.
For example, if client 1 logs into the application, I want to import client1.css. if client 2 logs into the application, I want to import client2.css. I will know the client number once I have validated the login information.
The application contains multiple .js files. Every .js file contains the following at the top of the file
import React from 'react';
import { Redirect } from 'react-router-dom';
import {mqRequest} from '../functions/commonFunctions.js';
import '../styles/app.css';
Is there a way to import .css files dynamically for this scenario as opposed to specifying the .css file in the above import statement?
Thank you
Easy - i've delt with similar before.
componentWillMount() {
if(this.props.css1 === true) {
require('style1.css');
} else {
require('style2.css');
}
}
Consider using a cssInJs solution. Popular libraries are: emotion and styled-components but there are others as well.
I generally recommend a cssInJs solution, but for what you are trying to do it is especially useful.
In Emotion for example they have a tool specifically build for this purpose - the contextTheme.
What cssInJs basically means is that instead of using different static css files, use all the power of Javascript, to generate the needed css rules from your javascript code.
A bit late to the party, I want to expand on #Harmenx answer.
require works in development environments only, once it goes to production you're likely to get errors or not see the css file at all. Here are some options if you, or others, encounter this:
Option 1: Using css modules, assign a variable of styles with the response from the import based on the condition.
let styles;
if(this.props.css1 === true) {
//require('style1.css');
import("./style1.module.css").then((res) => { styles = res;});
} else {
//require('style2.css');
import("./style2.module.css").then((res) => { styles = res;});
}
...
<div className={styles.divClass}>...</div>
...
Option 2: using Suspend and lazy load from react
// STEP 1: create components for each stylesheet
// styles1.js
import React from "react";
import "./styles1.css";
export const Style1Variables = (React.FC = () => <></>);
export default Style1Variables ;
// styles2.js
import React from "react";
import "./styles2.css";
export const Style2Variables = (React.FC = () => <></>);
export default Style2Variables ;
// STEP 2: setup your conditional rendering component
import React, {lazy, Suspense} from "react";
const Styles1= lazy(() => import("./styles1"));
const Styles2= lazy(() => import("./styles2"));
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
{isClient1() ? <Styles1 /> : <Styles2/>}
</Suspense>
{children}
</>
);
};
// STEP 3: Wrap your app
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
Option 3: Use React Helm which will include a link to the stylesheet in the header based on a conditional
class App extends Component {
render() {
<>
<Helmet>
<link
type="text/css"
rel="stylesheet"
href={isClient1() ? "./styles1.css" : "./styles2.css"}
/>
</Helmet>
...
</>
}
}
Personally, I like option 2 because you can set a variable whichClientIsThis() then modify the code to:
import React, {lazy, Suspense} from "react";
let clientID = whichClientIsThis();
const Styles= lazy(() => import("./`${clientID}`.css")); // change your variable filenames to match the client id.
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
<Styles />
</Suspense>
{children}
</>
);
};
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
This way you don't need any conditionals. I'd still recommend lazy loading and suspending so the app has time to get the id and make the "decision" on which stylesheet to bring in.

JssProvider in Material-UI isn't applying my custom production prefix to CSS

I've built a fairly simple React app based on create-react-app which uses the Material-UI for its interface components. It also depends on one of my own packages which also uses Material-UI (same version) for a couple of shared components.
Things were looking good locally until I ran a production build and deployed it. Some of the styles were behaving oddly, for example the Material-UI grid was much narrower than when running locally.
I did some reading and found a few instances of people discussing colliding class names under my scenario. This took me to some official Material-UI documentation which provides the following example code to use a custom class name prefix:
import JssProvider from 'react-jss/lib/JssProvider';
import { createGenerateClassName } from '#material-ui/core/styles';
const generateClassName = createGenerateClassName({
dangerouslyUseGlobalCSS: true,
productionPrefix: 'c',
});
function App() {
return (
<JssProvider generateClassName={generateClassName}>
...
</JssProvider>
);
}
export default App;
Before applying this fix when inspecting my production app's source code I could see the outermost DIV using the CSS class jss2 jss24.
After applying this fix my production app actually visually renders the same layout as my development version and so would appear to be fixed. However, examining the source shows the outermost DIV to have the class MuiGrid-container-2 MuiGrid-spacing-xs-8-24 which suggests to me something isn't right. I could leave it like this but it does mean I'm running with unoptimised code.
Am I doing something wrong here? Or is there an alternative resolution? I'm using current latest version of #material-ui/core (3.3.2) and the full contents of my App.js are:
import React, { Component } from 'react';
import { Provider } from "react-redux";
import { OidcProvider } from 'redux-oidc';
import JssProvider from 'react-jss/lib/JssProvider';
import Routes from './routes';
import store from './store';
import userManager from './utils/userManager';
import {
CustomUiTheme as Theme,
CustomUiLayout as Layout,
CustomUiSnackbar as Snackbar,
CustomUiModalAlert as Alert
} from 'custom-ui';
import Loading from './components/loading';
import { createGenerateClassName } from '#material-ui/core/styles';
const generateClassName = createGenerateClassName({
dangerouslyUseGlobalCSS: true,
productionPrefix: 'tw',
});
class App extends Component {
render() {
return (
<JssProvider generateClassName={generateClassName}>
<Provider store={store}>
<OidcProvider store={store} userManager={userManager}>
<Theme>
<Loading />
<Layout variant="xmas">
<Alert />
<Routes />
<Snackbar />
</Layout>
</Theme>
</OidcProvider>
</Provider>
</JssProvider>
);
}
}
export default App;

conditional css in create-react-app

I have default css file and separate css file that should be applied (to owerride default) only when certain conditions are met.
I am using create-react-app wit default import 'file.css' syntax.
What is the best way forward to decide whether to load or not load particular css file dynamically?
The require method only worked in development (as all the CSS is bundled upon build), and the import method did not work at all (using CRA version 3.3).
In our case, we have multiple themes, which cannot be bundled - so we solved this using React.lazy and React.Suspense.
We have the ThemeSelector, which loads the correct css conditionally.
import React from 'react';
/**
* The theme components only imports it's theme CSS-file. These components are lazy
* loaded, to enable "code splitting" (in order to avoid the themes being bundled together)
*/
const Theme1 = React.lazy(() => import('./Theme1'));
const Theme2 = React.lazy(() => import('./Theme2'));
const ThemeSelector: React.FC = ({ children }) => (
<>
{/* Conditionally render theme, based on the current client context */}
<React.Suspense fallback={() => null}>
{shouldRenderTheme1 && <Theme1 />}
{shouldRenderTheme2 && <Theme2 />}
</React.Suspense>
{/* Render children immediately! */}
{children}
</>
);
export default ThemeSelector;
The Theme component's only job, is to import the correct css file:
import * as React from 'react';
// 👇 Only important line - as this component should be lazy-loaded,
// to enable code - splitting for this CSS.
import 'theme1.css';
const Theme1: React.FC = () => <></>;
export default Theme1;
The ThemeSelector should wrap the App component, in the src/index.tsx:
import React from 'react';
import ReactDOM from 'react-dom';
import ThemeSelector from 'themes/ThemeSelector';
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
As I understand, this forces each Theme to be split into separate bundles (effectively also splitting CSS).
As mentioned in the comments, this solution does not present an easy way of switching themes runtime. This solution focuses on splitting themes into separate bundles.
If you already got themes split into separate CSS files, and you want to swap themes runtime, you might want to look at a solution using ReactHelmet (illustrated by #Alexander Ladonin's answer below)
You can use require('file.css') syntax instead. This will allow you to put it inside of a conditional.
e.g.
if(someCondition) {
require('file.css');
}
Use React Helmet. It adds links, meta tags etc into document header dynamically.
Add it into any render method.
import {Component} from 'react';
import ReactHelmet from 'react-helmet';
class Example extends Component{
render(
<ReactHelmet link={
[{"rel": "stylesheet", type:"text/css", "href": "/style.css"}]
}/>);
}
}
You can rewrite it on next <ReactHelmet/> rendering.
One simple solution that I found that works in production is to use vercel's styled-jsx. First, install styled-jsx:
npm install --save styled-jsx
Or if you use Yarn:
yarn add styled-jsx
Now create strings from your css file, so for instance:
const style1 = `
div {
display: flex;
flex-direction: column;
align-items: center;
}
`
const style2 = `
div {
display: flex;
flex-direction: column;
align-items: center;
}
`
And then in your React Component, you can do something like this:
const MyComponent = () => {
return (
<div className='my-component'>
<style jsx>
{
conditionA ? style1: style2
}
</style>
</div>
)
}
Simply add <style jsx>{your_css_string}</style> to the component which you wish to add styling to and you can then to implement conditions just use different strings to import different css styling.
If you are here you most likely are trying to condition a CSS or SCSS import, probably to make some light/dark mode theme or something. The accepted answer works just on mount, after the second css is loaded they are both loaded and you dont have a way to unload them, or actually you have, keep reading...
The use of React lazy and suspense is awesome but in this case we need to help our selves from webpack, because is actually the guy that bundles stuff and can also unbundle stuff, which is what you need, a toggle of css imports basically
Adding webpack lazyStyleTag
Go to your webpack config file and add the following rules
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
// Probly you already have this rule, add this line
exclude: /\.lazy\.css$/i,
use: ["style-loader", "css-loader"],
},
// And add this rule
{
test: /\.lazy\.css$/i,
use: [
{ loader: "style-loader", options: { injectType: "lazyStyleTag" } },
"css-loader",
],
},
],
},
};
Now take your CSS files and change their name to the lazy named convention
You probably have this
styles.css
// or
styles.min.css
Now will be this:
styles.lazy.css
Then create your React theme Provider in a simple React context, this context will wrap your App so it will load the conditioned CSS everytime the context state changes. This context state is going to be availabe anywhere inside your app as well as the setter via a custom hook we will export from the same file, check this out:
import React, {
useEffect, createContext, useState, useContext,
} from 'react';
import { Nullable } from 'types';
// Import both files here like this:
// Import of CSS file number 1
import LightMode from './theme/styles.lazy.css';
// Import of CSS file number 2
import DarkMode from './theme/styles.lazy.css';
interface IContext {
theme: Nullable<string>
toggleTheme: () => void
}
const Context = createContext<IContext>({
theme: null,
toggleTheme: () => { },
});
// Your Provider component that returns the Context.Provider
// Let's also play with the sessionStorage, so this state doesn't
// brake with browser refresh or logouts
const ThemeProvider: React.FC = ({ children }) => {
// Im initialazing here the state with any existing value in the
//sessionStorage, or not...
const [theme, setTheme] = useState<Nullable<string>>(sessionStorage.getItem('themeMode') || 'dark');
// this setter Fn we can pass down to anywhere
const toggleTheme = () => {
const newThemeValue = theme === 'dark' ? 'light' : 'dark';
setTheme(newThemeValue);
sessionStorage.setItem('themeMode', newThemeValue);
};
// Now the magic, this lazy css files you can use or unuse
// This is exactly what you need, import the CSS but also unimport
// the one you had imported before. An actual toggle of import in a
// dynamic way.. brought to you by webpack
useEffect(() => {
if (theme === 'light') {
DarkMode.unuse();
LightMode.use();
} else if (theme == 'dark') {
LightMode.unuse();
DarkMode.use();
}
}, [theme]);
return (
<Context.Provider value={{ theme, toggleTheme }}>
{children}
</Context.Provider>
);
};
export default ThemeProvider;
// This useTheme hook will give you the context anywhere to set the state of // theme and this will toggle the styles imported
export const useTheme = () => useContext(Context);
Remember to put this state on the sessionStorage like in this example so your user has the state available every time it comes back or refreshes the page
Don't forget to wrap the friking App with the Provider:
import ThemeProvider from './ThemeProvider'
const App = () => {
return (
<ThemeProvider>
<App />
</ThemeProvider>
)
}
Now just toggle the CSS imports of your application using your cool useTheme hook
import { useTheme } from './yourContextFile';
// inside your component
const AnyComponentDownTheTree = () => {
const { theme, toggleTheme } = useTheme()
// use the toggleTheme function to toggle and the theme actual value
// for your components, you might need disable something or set active a
// switch, etc, etc
}
Other solution does not work for me. After one day of the search, I obtain bellow solution. In my issue, I have two CSS files for RTL or LTR like app.rtl.css or app.ltr.css
Create a functional component Style like this:
import React, { useState } from "react";
export default function Style(props) {
const [stylePath, setStylePath] = useState(props.path);
return (
<div>
<link rel="stylesheet" type="text/css" href={stylePath} />
</div>
);
}
And then you can call it, for example in App.js:
function App() {
...
return (
<Style path={`/css/app.${direction}.css`} />
)}
direction param contains rtl or ltr and determine which file should be loaded.
I tested some alternatives available in some tutorials and the best for me was to use only classes in css.
One of the problems I encountered when using
require: did not override on some occasions
import: delay generated to load css
The best way for me was to actually put a class switch
.default-sidebar {
--side-text-icon:rgba(255,255,255,.9) !important;
--side-text-section: rgb(255,255,255,.8) !important;
--side-separator-section:#ff944d !important;
}
.dark-sidebar {
--side-text-icon:rgba(255,255,255,.9) !important;
--side-text-section: rgb(255,255,255,.8) !important;
--side-separator-section:#262626 !important;
}
'
<div className={`root-sidebar ${condition?'default-sidebar':'dark-sidebar'}`}></div>

Resources