I am working on ReactJs for a will. I used Typescript in both client and server side for development. I used npm run build to build my project, I will deliver only the build folder now.
Client:
Client
build
-static
-index.html
Server
node_modules
src
package.json
package-lock.json
tsconfig.json
everything is file, For few cases I need get ReactjsComponents from server. These new Components are created by each user.
New ReactJs
ButtonList
ButtonListComponent.tsx
index.ts
IProps.ts
I converted .tsx file to .js using babel
ButtonList
ButtonListComponent.d.ts
ButtonListComponent.js
index.d.ts
index.js
IProps.d.ts
IProps.js
And In server I rendered to plain html and sent the html as string to client and printed.
ReactHelper.ts
import React from 'react'
import * as ReactDOMServer from 'react-dom/server'
import ButtonList from '../../NewComponents/ButtonList'
export function ExportReactComponent(){
var reactHtml = ReactDOMServer.renderToStaticMarkup(ButtonList);
return reactHtml
}
But I used functional Components and materialUI the hooks are not working. So instead of create reactComponents and converting to js. I wrote Plain html and sent to client for printing. but in those case scripts are not working.
ReactHelper.ts
export function ExportReactComponent(){
var htmlInput = `<h1>The script element</h1>
<p id="demo"></p>
<script>
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
`;
return htmlInput
}
someone help me, if tried these things but not works as expected, is there any other good solution to this? Thanks in advance.
Git repo: https://github.com/nivedhauswin/ReactApp.git
I used Iframe to display user components. I want to track location change in Iframe contentWindow.location.href but i get
SecurityError: Blocked a frame with origin "http://localhost:3000" from accessing a cross-origin frame.
Related
The React app described below is built in VS Code with React v18.0.0, node v.14.15.4 and npm v8.4.1. It also imports modules from a 3rd party mapping API called the ArcGIS JavaScript API and I'm running v4.18 of that.
I've used this mapping API to create many vanilla JS web apps over the years. I reference its JS and CSS via CDN. The current need is to leverage it within a React app that's deployed in a Docker container running NGINX.
I can launch the app from VS Code using 'npm start'. It runs at localhost:3000 and everything works as expected. The map renders within the correct div and it shows the gray canvas basemap tiles. The CSS is also loading correctly and the zoom +/- buttons render correctly. All of this correct behavior happens when the app is launched locally. I can deploy it IIS or I can just run 'npm start' in VS Code and it runs without issues. This is how it displays:
Code within the React app that creates the map and map view:
import logo from './logo.svg';
import './App.css';
import { useEffect, useState, useRef } from "react";
import esriConfig from "#arcgis/core/config";
import Map from "#arcgis/core/Map";
import MapView from "#arcgis/core/views/MapView";
import FeatureLayer from "#arcgis/core/layers/FeatureLayer";
function App() {
const mapRef = useRef();
esriConfig.apiKey = "REMOVED";
const initMap = () => {
const map = new Map({
basemap: "arcgis-light-gray",
layers: []
});
const view = new MapView({
map: map,
center: [-82.354, 27.895],
zoom: 9,
container: mapRef.current,
});
};
useEffect(() => {
initMap();
}, []);
return (
<div className="App">
<div id="map-div" ref={mapRef}>
</div>
</div>
);
}
export default App;
All of the above works when hosted locally or builT and deployed to IIS.
But the ultimate goal is to deploy this app within a Docker container running NGINX. The docker image builds successfully and also runs successfully, but when I browse the app running inside the container the basemap tiles don't render. CSS and JS are both being loaded, I get 200 responses for both. The 3rd party content is loading but for some reason the images tiles in the map don't render and all I get is a map with a blank background.
I get the same result in Chrome and Edge. There are no errors or exception thrown in the browser's console. It's as if the JS API is loading successfully but somehow the image tiles aren't rendering and I can't see any reason why this would fail only inside a Docker container.
Dockerfile
FROM node:latest as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:latest
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/build/ /usr/share/nginx/html/
.dockerignore file
node_modules
Dockerfile
.git
default.conf for NGINX
server {
listen 80;
listen [::]:80;
root /usr/share/nginx/html;
index index.html index.htm index.nginx-debian.html;
server_name localhost;
location / {
try_files $uri $uri/ =404;
}
}
Because I have other React apps with ArcGIS JS maps working perfectly, I believe the problem has to have something to do with Docker and/or NGINXand how it is failing to handle the image tiles correctly.
I also noticed some differences in the requests being made by each version of this app. In the network tab of Chrome's developer tools it looks like the app makes an extra call when running locally that is not made when it's running in a docker container.
Locally it makes two calls to LightGray:Base and LightGray:Labels, and also World_Basemap_v2 further down.
I see fewer calls for the light gray basemap resource in the Docker container and no request for World_Basemap_v2 at all.
Unfortunately I don't know if the above differences are significant at all.
Has anyone else had any issues along these lines? I've already called for help on the ArcGIS forum but I sense that almost nobody is trying to run this API within Docker.
Thanks
Update & Work-Around
Uninstalling #arcgis/core and installing esri-loader instead resulted in correct rendering of the basemap I'm using.
import logo from "./logo.svg";
import "./App.css";
import { loadModules } from "esri-loader";
function App() {
loadModules(['esri/config', 'esri/views/MapView', 'esri/Map']).then(([esriConfig, MapView, Map]) => {
esriConfig.apiKey = "REMOVED";
const map = new Map({
basemap: 'arcgis-light-gray'
});
const mapView = new MapView({
map: map,
zoom: 10,
center: [-82.5, 28],
container: "map-div"
});
});
return (
<div className="App">
<div id="map-div"></div>
</div>
);
}
export default App;
I found two work-arounds.
The problem seems to be with the esriConfig module, specifically defining an API key within a React app. A key is required to use the premium set of basemaps which includes the one I'm using, "arcgis-light-gray". I have always defined my developer API key in my ArcGIS JS apps and I've done so in this React app as well. But this module appears to be broken in React. Either that or I need to reference the module differently than I currently am. If so, it's not covered in any documentation I could find.
Work-around #1 - use a non-premium basemap like "gray" or "gray-vector". These basemaps lack the same level of detail at higher zoom levels, making this approach unacceptable if you want to zoom in to larger scales and see detail on individual parcels.
Work-around #2 - uninstall #arcgis/core and install esri-loader instead. This set of modules appears to be the better way to use the ArcGIS JS API in React. With esri-loader the modules are imported with the loadModules function instead of require or import. After refactoring the app to use esri-loader I am now able to define my API key and the premium "arcgis-light-gray" basemap renders correctly. I'll put updated code in the original post.
I have a CRA and want to have the first page generated statically to improve load time and SEO. The idea is to run a NodeJS script that renders the App document inside index.html.
Here is my code:
const { renderToString } = require("react-dom/server");
const App = require('./App');
const content = `<html><body>${renderToString(App)}</body></html>`
const fs = require('fs');
fs.writeFileSync('../public/index.html', content);
However, I have problems running it with NodeJS:
import React from 'react';
^^^^^^
SyntaxError: Cannot use import statement outside a module
Apparently, I have to build the source, but I don't know how to do it. I'm using CRA to build my React files (actually, I'm using react-app-rewired, so I could customize the build process if I only knew how to do it).
What should I do to make it work?
the error is from node: https://stackoverflow.com/a/59399717/13216797
I know it's not your question... but what about using Nextjs?
Even without a node environment you can use the "next export" command to make a bundle that will work as it would static files while still being react...
I ended up using puppeteer and just saving the generated client-side code. Before that I spent hours stubbing out first window, then window.location, then window.localStorage, window.document, window.document.createElement, it never ended. With puppeteer, it was pretty easy.
Since I can't use browser's pdf viewer in the network where the app is going to be used, I am testing a react-pdf package for loading PDF's with React.
I have made a component where I am sending a url of my PDF that I get from backend:
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf';
const PDFViewer = ({url}) => {
const [numPages, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
}
function onLoadError(error) {
console.log(error);
}
function onSourceError(error) {
console.log(error);
}
return (
<div>
<Document
file={window.location.origin + url}
onLoadSuccess={onDocumentLoadSuccess}
onLoadError={onLoadError}
onSourceError={onSourceError}
>
{[...Array(numPages).keys()].map((p) => (
<Page pageNumber={p + 1} />
))}
</Document>
</div>
);
};
export default PDFViewer;
But, on opening the PDFViewer I get an error
Error: Setting up fake worker failed: "Cannot read property 'WorkerMessageHandler' of undefined"
In documentation it says that you should set up service worker and that the recommended way is to do that with CDN:
import { pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;
But, I can't use CDN links for my project, and in the documentation it also says:
Create React App uses Webpack under the hood, but instructions for Webpack will not work. Standard instructions apply.
Standard (Browserify and others)
If you use Browserify or other bundling tools, you will have to make sure on your own that pdf.worker.js file from pdfjs-dist/build is copied to your project's output folder.
There are no instructions on how to do that with create-react-app. How can I set this up locally then?
Install pdfjs-dist
import { Document, Page, pdfjs } from "react-pdf";
import pdfjsWorker from "pdfjs-dist/build/pdf.worker.entry";
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
Reference: https://github.com/mozilla/pdf.js/issues/8305
found a more efficient way of including the worker
by including the library from the dependencies of react-pdf itself, this way you will never get a version mismatch like this The API version "2.3.45" does not match the Worker version "2.1.266"
if you install pdfjs-dist manually you will have to check react pdf dependency version on every build
import { Document, Page, pdfjs } from "react-pdf";
import pdfjsWorker from "react-pdf/node_modules/pdfjs-dist/build/pdf.worker.entry";
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
see similar error on pdfjs library : https://github.com/mozilla/pdf.js/issues/10997
hope it helps people
You can install worker loader module for webpack:
npm install worker-loader --save-dev
Then use it inline where you are going to work with a worker:
import SomeWorker from 'worker-loader?inline=true!../workers/some.worker'
const someWorker: Worker = new SomeWorker()
someWorker.postMessage(...)
I haven't tried this solution with react-pdf, but it might help.
You may need to add types for TypeScript if you are using it:
declare module 'worker-loader*' {
class SomeWorker extends Worker {
constructor()
}
export default SomeWorker
}
Just to add that in some .d.ts file in your project.
Install pdfjs-dist then use the webpack module:
import { pdfjs } from 'react-pdf'
import worker from 'pdfjs-dist/webpack'
pdfjs.GlobalWorkerOptions.workerSrc = worker
If your build process uses cli commands, (i.e. AWS buildspec), you can use this:
mkdir -p build && cp ./node_modules/pdfjs-dist/build/pdf.worker.js build
If you are in a corporate codebase environment and have little to no experience configuring WebPack, I wanted to share a little more info if (like me) you struggled with this for quite a long time.
My environment has several complicated WebPack config files (base, production, and development), and the resolution ended up being pretty simple but it escaped me for quite a while because I was unfamiliar with the complicated build process.
1) The Implementation
Quite simple, just as the docs recommend (I went with the minified file). Our React environment required me to use React-PDF#4.2.0, but there aren't any differences here.
import {Document, Page, pdfjs} from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = 'pdf.worker.min.js'
Note: a previous solution recommended grabbing the source from the react-pdf node_modules folder, however, my codebase is setup to install dependencies separately somehow because when I npm install react-pdf, pdfjs-dist is also installed separately. Regardless, this method did not work for my codebase (importing the worker as a variable) due to the way the project is built. The import command acted like it couldn't find the proper named export inside a node_modules folder. It was top-level or nothing.
2) WebPack Config
Since I do not know WebPack at all, but found pretty easily that what I needed to do was take advantage of CopyWebpackPlugin, I searched through those existing dev and prod webpack config files, and found existing copy commands for JQuery and polyfill and added a new plugin to that array:
new CopyWebpackPlugin({from: 'node_modules/pdfjs-dist/build/pdf.worker.min.js})
I had to do this in multiples places in both config files as this large project has several entry point server files for the different services of the website.
3) Inserting Script Tag to HTML Head
This was the crucial part I was missing. There was a "ComponentFactory" file whose job it was to insert chunks of html in the <head> and tail of the html file. I wasn't used to something like this on small projects. So there, I simply copied what was already done for the jquery and polyfill, which included a string literal of the location of the assets folder the webpack was building out to. In my case, that was something like "assets/v1/". So the tag looked like this:
<script src=`${STATIC_ASSETS_URL}/pdf.worker.min.js` defer></script>
It works perfectly, however I am still getting the "Setting Up a Fake Worker" but immediately after that, it loaded it successfully in console and checking the dev tools, it was using the proper file. It's probably just a timing thing of the src set not running high enough in the code, but it was not effecting the outcome, so I let it go.
(Sidebar, if you also get the "TT unknown function" (paraphrasing) error, that can be ignored. It's just a font issue with whatever PDF you are loading and is just a warning, not an error.)
I was facing this issue once I had to use "react-pdf" from within a package.
It was solved by importing the worker conditionally into the code:
Conditional import:
export const getWorker = () => {
try {
return require('react-pdf/node_modules/pdfjs-dist/legacy/build/pdf.worker.entry.js')
} catch () {
return require('pdfjs-dist/legacy/build/pdf.worker.entry.js')
}
}
usage:
import { Document, Page, pdfjs } from 'react-pdf/dist/umd/entry.webpack'
pdfjs.GlobalWorkerOptions.workerSrc = getWorker()
I started learning react. Started a React app with my commant prompt, everything is downloaded correctly, but the app that was started does not contain components. Every tutorial I try their apps start with this line in App.js file import React, {Component} from 'react'; but I don't have that.
Components are actually started with 'class App extends Component', but I dont have that. There is also no render method in this component.
I do not know what to try.
function App () {
return (...)
}
Follow this path if you copied your project from another external source :
Create a folder
Go to cmd of the current folder
In your cmd write:
create-react-app myapp
When the download finished,
In cmd write code .(this opens visual studio code and opens your react files)
Or simply open your editor and open folder in it
In cmd (terminal) of your project folder write npm start, and your app will load on localhost:3000
finally answer of your question You can edit your project, by going to src folder and editing app.js
update
* I checked create react app myself it seems In the new version of create-react-app
It doesn't create class-based component anymore (it creates a function based component) and you need to change it to class-based component manually if you want to.*
Update2, to change function based to class ,delete app.js codes and replace these:
Import React , { Component } from "react"
class App extends Component {
Render() {
return <div> test </div>;
}}
export default App;
Hope this helps you
I used create-react-app to play with react. I noticed that no matter what I type in after localhost:3000/ (which is the default url on dev machine) I always get the content from localhost:3000
This means I cannot access images so that sort of means I can't really do anything with it.
Is this a bug or is it some awesome new feature that I just don't understand? How can this be switched off?
Create React App uses historyApiFallback for the webpack-dev-server, which means that all requests that give a 404 response will fall back to load the index.html file.
If you e.g. add an image to the public folder, you can see that the image takes precedence over the index.html file.
You can also import the image with the help of Webpack and use the import as src in your code, and Webpack will make sure the image ends up in the final build folder for you.
import img from './image.png';
you can use
render() {
return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;
}
for more information please refer Adding Assets Outside of the Module System