I'm using Webpack Module Federation to create 2 React applications: host and child.
In the host, I create atoms.ts and selector.ts filed and I expose them via the plugin under the expose section:
exposes: {
"./atoms": "./src/recoil/atoms.ts",
"./selectors": "./src/recoil/selectors.ts",
}
Inside the child, I just consume that via the remotes section:
remotes: {
host: "host#http://localhost:3000/remoteEntry.js",
}
Then, in the code of the child I use that like that:
import {someSelector} from "host/selectors"
const val = useRecoilValue(someSelector);
It's working fine but I got this warning in the console:
Duplicate atom key "userAuthState". This is a FATAL ERROR in
production. But it is safe to ignore this warning if it occurred because of
hot module replacement.
Does anyone face that issue and know if it's really a problem or how we could hide the warning?
Another related q:
Is it ok that the host will contain <RecoilRoot> and also the child will contain <RecoilRoot> ? because I want both will manage their own state but also share atom/selectors.
Thanks!
Regarding your second question:
Yes, this is totally fine. The nested <RecoilRoot> will create its own context and every atom referenced below the second root will be independent from the upper root. This is also explained in the docs.
Regarding the first question: As the log states this is fine as long as it occurs during development. Sometimes during the hot module replacement recoil throws away atoms and reinstantiates them causing this duplication to happen internally.
But as long as this warning doesn't pop up in your production code, everything is fine.
Are you importing the atoms or the selectors in your host application using a local path?
You need to include in your host webpack config its own entrypoint as remote and import your atoms from 'host/atoms'
I think this could solve your issue.
You can ignore the warning output (if you can bare it), functionality is not affected.
Also, you can install the intercept-stdout package and add the following to next.config.js (outside of the exported configuration):
const intercept = require("intercept-stdout")
// safely ignore recoil warning messages in dev (triggered by HMR)
function interceptStdout(text) {
if (text.includes("Duplicate atom key")) {
return "";
}
return text;
}
if (process.env.NODE_ENV === "development") {
intercept(interceptStdout);
}
This way can omit the annoying warning in console.
Related
I am writing a React+react-query application today and I am running into a problem when I switched from development mode to production mode.
Production mode is a mix of running npm run build and serving those HTML/JavaScript/CSS files inside a serve (maybe using npx serve dist). Development mode is just running npm run dev (or npm start if using CRA).
This is the code:
const useMyQuery = () =>
useQuery(
["myAwesomeData"],
async () => {
const { data } = await axios.get(`http://http.us/500`);
return data;
},
{ placeholderData: { myAwesomeProperty: [] } }
);
const App = () => {
const { isError } = useMyQuery();
return isError ? <div>Error</div> : <Child />;
};
const Child = () => {
const { data } = useMyQuery();
return (
<pre>
<code>{JSON.stringify(data.myAwesomeProperty)}</code>
</pre>
);
};
For the sake of simplicity, I omitted QueryClientProvider and its client. Also placeholderData is being used as a loading state, which allows Child to be properly rendered while real data is loading.
The most important part here is data.myAwesomeProperty AFTER the loading state - which throws an error ONLY when running in production. The error produced is:
TypeError: Cannot read property 'myAwesomeProperty' of undefined
When running on development, the <div>Error</div> appears as expected.
My main concern here is NOT solve the problem (as I could simply add a if statement on Child component to prevent accessing data.myAwesomeProperty).
Instead, I would like to know why there is a difference between development's and production's behavior? Is it documented somewhere?
The dev and prod version of React has quite a bit difference. But the main one can be the speed here. In general the prod version is a lot more performant.
Based on the way you write, there's no way you should expect a valid myAwesomeProperty. So in the prod, you get the error right away.
In the dev, maybe things slow down, when you get the Child, the API already finishes.
NOTE: list coupe of dev/prod difference.
profiler enabled to collect time info
debug logger, adds quite a bit name and check
check hooks count and order match
strict mode adds bunch of stuff, https://reactjs.org/docs/strict-mode.html
I also notice in the strict mode, things can be rendered twice, My React Component is rendering twice because of Strict Mode
This might answer if you don't get an error, since the double render actually happened in one update (not two updates).
Recently, React started giving depreciation warnings for componentWillReceiveProps lifecycle method. I am using a library that utilized this function and the maintainers haven't updated their codebase yet.
Currently, any time I run my tests, whether it is in development or in CI, I keep getting ~30 lines of depreciation warnings for each component that the maintainer provides.
Is there a way to suppress these warnings (at least in development)?
EDIT:
I am willing to add certain comments in my files to disable warnings from a specific package if there is a chance:
// some line to disable warnings for this package
import { DateRangePicker } from 'react-dates';
If you want to disable all warnings that meet some condition, keeping all other warnings, for all tests:
const originalWarn = console.warn.bind(console.warn)
beforeAll(() => {
console.warn = (msg) =>
!msg.toString().includes('componentWillReceiveProps') && originalWarn(msg)
})
afterAll(() => {
console.warn = originalWarn
})
React codebase also contains expect(render(...)).toWarnDev(...), but that's not included in Jest documentation, you might need to investigate more if you want to use that feature.
Similar in concept to a previous answer, but a bit easier would be:
jest.spyOn(global.console, 'warn').mockImplementationOnce((message) => {
if (!message.includes('componentWillReceiveProps')) {
global.console.warn(message);
}
});
If you wanted to do it across tests you could do:
let consoleSpy;
beforeAll(() => {
consoleSpy = jest.spyOn(global.console, 'warn').mockImplementation((message) => {
// same implementation as above
});
afterAll(() => consoleSpy.mockRestore());
A variation on #Aprillion's answer...
I wanted to suppress certain error messages in all tests (in a create-react-app application).
I added this to my setupTests.js:
// This error is a bug fixed in React 18: https://github.com/facebook/react/pull/22114.
// Suppress it for all tests.
const BOGUS_UNMOUNTED_ERROR = (
"Can't perform a React state update on an unmounted component."
);
const originalError = console.error.bind(console.error);
console.error = (...args) => !args.toString().includes(BOGUS_UNMOUNTED_ERROR)
&& originalError(...args);
I guess the most important difference is that I replaced (msg) with (...args) in two places, so that all arguments to console.error() were passed through. Without this, I was getting %s's in my console error messages that should have been filled in with other arguments.
So I found a way to fix these warnings for any library using react-codemod.
Since this library does not work inside node_modules so have to do a little hack.
Run:
yarn add -D react-codemod
open ./node_modules/react-codemod/bin/cli.js
Remove/comment line (72) > + // args.push('--ignore-pattern=/node_modules/');
Run:
./node_modules/.bin/react-codemod rename-unsafe-lifecycles
To this question answer with the path of the library, you want to fix...
“On which files or directory should the codemods be applied?”
./node_modules/react-dates/lib/** // or any library with issue
This could be a temporary fix till react-codemod support node_modules libraries.
You can also fork the library and remove the line for yourself and use it like this inside your CI pipeline to not get any warnings like this anymore.
You can just add this line above whatever is showing the warning. I have tried this in VScode so i'm not sure if it works with others. This disables whatever warning that will be displayed in the next line.
// eslint-disable-next-line
I should preface this by saying that I know next to nothing about the difference between the project I started using npx create-react-app and the project I started using touch app.js touch app.html.
I have the following code:
var departments = {
id0: "Networking",
id1: "Video",
id2: "Desktop PCs",
}
departments.list = function () {
var output = "";
for (name in departments) {
if (typeof departments[name] == 'string') {
output=output+departments[name].valueOf()+", ";
}
}
return(output);
}
When I create a 'dumb' javascript app using the above-mentioned touch approach, invoking departments.list() returns Networking, Video, Desktop PCs, as I would expect. However, pasting this exact code into an unenclosed section of my App.js file created by npx create-react-app yields the compiler error Unexpected use of 'name' no-restricted-globals on each line containing the word name.
I was under the impression that any Javascript expressions are valid in React, as React is strictly a superset of Javascript? Why does this happen? Am I wrong?
I also recognize that I'm working with a lot of systems that I don't understand, but I don't even understand them well enough to start researching them yet. Npx, npm, webpack, nodejs and their relationships to Javascript and React are all mysterious to me.
You need the let keyword before your name variable.
for (let name in departments) {
...
}
When you assign to an undeclared variable in JavaScript, it manipulates the global scope. If you're using strict mode, trying to assign to a undeclared variable throws an error.
More info here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode/Transitioning_to_strict_mode#New_runtime_errors
I've successfully instantiated a simple AudioWorklet in React and wish to start a simple oscillator like in Google's example. In order to test run it, I am rendering a button whose onClick event calls the following:
src/App.jsx:
userGesture(){
//create a new AudioContext
this.context = new AudioContext();
//Add our Processor module to the AudioWorklet
this.context.audioWorklet.addModule('worklet/processor.js').then(() => {
//Create an oscillator and run it through the processor
let oscillator = new OscillatorNode(this.context);
let bypasser = new MyWorkletNode(this.context, 'my-worklet-processor');
//Connect to the context's destination and start
oscillator.connect(bypasser).connect(this.context.destination);
oscillator.start();
})
.catch((e => console.log(e)))
}
The problem is, on every click, addModule method is returning the following error:
DOMException: The user aborted a request.
I am running Chrome v66 on Ubuntu v16.0.4.
src/worklet/worklet-node.js:
export default class MyWorkletNode extends window.AudioWorkletNode {
constructor(context) {
super(context, 'my-worklet-processor');
}
}
src/worklet/processor.js
class MyWorkletProcessor extends AudioWorkletProcessor {
constructor() {
super();
}
process(inputs, outputs) {
let input = inputs[0];
let output = outputs[0];
for (let channel = 0; channel < output.length; ++channel) {
output[channel].set(input[channel]);
}
return true;
}
}
registerProcessor('my-worklet-processor', MyWorkletProcessor);
My code is straight JavaScript, not React, but I got the same error because the path provided to addModule was incorrect. In my case, both the script that calls addModule and the script provided as the argument to addModule reside in the same directory ("js"). In spite of that, I still had to include this directory in the path to eliminate the error:
...addModule('js/StreamTransmitter.js')...
I hope this helps. Good luck!
For anyone else getting this mysterious error, swallow your pride and check the following:
The processor doesn't have any errors.
The processor is calling external modules with proper path to the external file(s).
The external modules don't have any errors.
The promise will abort when external modules that are loaded via "import" have errors, or the paths to the modules can't be resolved (e.g. the path's to the modules are wrong and don't point to existing files).
This worked for me: serve your worklet files from public folder instead of src. The addModule(url) function points there by default, so addModule('worklets/foo.js') references file public\worklets\foo.js
Source: https://hackernoon.com/implementing-audioworklets-with-react-8a80a470474
This seems to be a bug in the Chromium module loader, it parses the worklet/processor.js file by removing whitespace, which in turn causes it to have JavaScript syntax errors everywhere, which then finally causes this generic non-explanatory error message to show up.
The solution is to serve your worklet-processors (e.g. worklet/processor.js in your case) with:
Content-Type: application/javascript
or
Content-Type: text/javascript
I also experienced this error but due to a Webpack issue.
Turns out webpack doesn't support worklets like it supports web workers (see this issue).
I would recommend using worker-url with webpack.
Install worker-url
npm i --save-dev worker-url
Update your webpack config to include the WorkerUrl plugin.
const WorkerUrlPlugin = require('worker-url/plugin');
module.exports = {
// ...
plugins: [new WorkerUrlPlugin()],
// ...
};
Use WorkerUrl like so:
import { WorkerUrl } from 'worker-url';
const workletUrl = new WorkerUrl(
new URL('./random-noise-processor', import.meta.url),
{ name: 'worklet' },
);
await context.audioWorklet.addModule(workletUrl);
The Error "DOMException: The user aborted a request." happens when the AudioWorklet.addModule() function cannot load the file from the path or URL you provided. Refer to this MDN page
The api AudioWorklet.addModule() expects a String containing the URL of a JavaScript file with the module to add.
It can be an internal URL that points to your public folder where the browser loads your static files in this case -> 'worklet/processor.js if the worklet folder is inside the public directory of your React app.
You can modify your code as below.
this.context.audioWorklet.addModule('worklet/processor.js')
In this case the audioWorklet.addModule() method expects the path to point to your public folder. It can also be an external URL for example a link to Github repository that loads the JS file.
Changing:
this.context.audioWorklet.addModule('worklet/processor.js')
with
this.context.audioWorklet.addModule('../worklet/processor.js')
worked for me.
I've been working on a project using Meteor and React, which needs a PDF viewer with the ability to select text.
I'm currently trying to achieve this with Mozilla's PDF.js, but am having some trouble getting started. I'm a long time reader, first time asker at stackoverflow.
I've installed PDF.js with npm.
npm install pdfjs-dist --save
Now I'm trying to modify the example from pdf.js's github project here to create a React component that will render a PDF from a supplied file path and include a text layer.
imports/ui/components/PDF/PDFText.jsx
import React from 'react';
require ('pdfjs-dist/build/pdf.combined');
require ('pdfjs-dist/web/compatibility');
export default class PDFText extends React.Component {
renderPDF() {
PDFJS.workerSrc = '/node_modules/pdfjs-dist/build/pdf.worker.js';
const container = document.getElementById('pdf-container');
const scale = 1;
const pageNumber = 1;
PDFJS.getDocument(this.props.file).then(function(pdf) {
return pdf.getPage(pageNumber).then(function(page) {
var pdfPageView = new PDFJS.PDFPageView({
container: container,
id: pageNumber,
scale: scale,
defaultViewport: page.getViewport(scale),
textLayerFactory: new PDFJS.DefaultTextLayerFactory()
});
pdfPageView.setPdfPage(page);
return pdfPageView.draw();
});
});
}
render() {
this.renderPDF()
return (
<div id='pdf-container'></div>
);
}
}
If I include this component in page I get the following error:
Uncaught (in promise) TypeError: PDFJS.DefaultTextLayerFactory is not a constructor
The next thing I tried was including 'pdfjs-dist/web/pdf_viewer' in my code, as this is where DefaultTextLayerFactory is declared. I modified the code above to add the following line above the class declaration:
require ('pdfjs-dist/web/pdf_viewer');
When I run the code now, I get a different error.
Uncaught TypeError: Cannot read property 'PDFJS' of undefined
at Object.<anonymous> (modules.js?hash=9dd20a3…:114918)
at __w_pdfjs_require__ (modules.js?hash=9dd20a3…:114838)
at Object.<anonymous> (modules.js?hash=9dd20a3…:117449)
at __w_pdfjs_require__ (modules.js?hash=9dd20a3…:114838)
at Object.<anonymous> (modules.js?hash=9dd20a3…:118157)
at __w_pdfjs_require__ (modules.js?hash=9dd20a3…:114838)
at module.exports (modules.js?hash=9dd20a3…:114884)
at modules.js?hash=9dd20a3…:114887
at webpackUniversalModuleDefinition (modules.js?hash=9dd20a3…:114811)
at pdf_viewer.js (modules.js?hash=9dd20a3…:114818)
I'm really unsure what is going on here. I noticed that the function complaining refers to webpack - which I haven't been using.
I've also tried including the following check at the start of my code (this is taken from pageviewer.js in the github link above).
if (!PDFJS.PDFViewer || !PDFJS.getDocument) {
alert('Please build the pdfjs-dist library using\n' +
' `gulp dist`');
}
My code does in fact trigger that alert (PDFJS.PDFViewer is undefined) but the message doesn't seem correct as I installed the built pdfjs-dist library using npm. That message seems for people who cloned the repo. There isn't a gulp file in the pdfjs-dist directory - which makes sense.
I'm sure part of thep problem is that I'm experimenting with a lot of new tools here. This is my first time working with meteor, react, node, and pdf.js, so apologies in advance if I've made an obvious rookie mistake.
For the record I've tried a few other libraries, including:
mikecousins/react-pdf-js (worked reasonably well for simply displaying a pdf with no text layer).
peerlibrary/meteor-pdf.js (I hit some errors with this one as well, and I didn't pursue it too much further as the repo hasn't been touched in a couple of years).
Hopefully that's enough information for someone to spot the issue. My theory is that there's some other set up step I need to do to get this working for meteor or react (and that's why it hasn't been obvious from the "getting started" in the PDF.js website.
Also, I'm not locked in to PDF.js, so if the easiest solution to my problem is to use something else, I'd be happy to try that.
Thanks for your time