The Environment
Next.js v13 (stable, no ./app folder) is running with Node v18 in Ubuntu WSL.
As per de docs , the Crypto API has been around since Node v14~ish.
This has been indeed tested in Node, in my environment:
Node 18 importing and running crypto.randomUUID()
I also printed the whole object and it looks as the docs says it should.
The Problem
Imagine this simple component:
import crypto from 'crypto';
export default function Crypto() {
console.log(crypto);
return (
<p>
{crypto.randomUUID()}
</p>
);
}
Next.js says it "compiled client and server successfully in 397 ms". But after the UUID renders in the browser for a couple of milliseconds, Next.js throws a couple of errors revolving around randomUUID not being a function.
Next Runtime Error with crypto.randomUUID()
I see that Webpack is somewhat mingling in there; haven't tried Turbopack. It's beyond the scope of this issue.
After commenting out the method invocation within the paragraph, the console.log(crypto) runs and prints twice, as usual, in the devtools as follows:
crypto method printed
Notice how one comes from "react devetools backend" and the other one from webkpack. That leads me to believe the error gets thrown server-side as the console.loglog is invoked before the UUID method.
Server-side, despite the errors thrown in the browser, the object gets printed by the Next CLI and it contains the method: Next CLI prints crypto object and randomUUID is listed
Client-side, within the printed object, the method randomUUID() is nowhere to be found:
Inside printed crypto object in devtools
This confirms the error message. My code is not getting access to the method. Also, a couple of methods are missing, when compared to the Node docs.
And yet if one console.log(crypto) directly from the devTools, it has indeed the method within its prototype:
randomUUID directly from devtools
Furthermore, because of the structure, I'm inclined to believe the crypto object being printed is somehow coming from Node, as the structure of the Chrome V8 crypto object is completely different. But why in the hell are those methods missing?
I tried console.loging the object server-side, client-side, and in-between. Somehow the method gets lost in-between. Webpack might be the culprit. Worst of all, albeit being for the blink of an eye, I can see the string rendered before the errors get thrown; and dismissing the error cards throws a blank body. The string disappears.
EDIT
The reason one imports/requires crypto is so it can run in Node. Next is a SSR framework; in a nutshell it is intended to run first on the server, get rendered and delivered as HTML as much as it can to the client. If not imported, Node throws an error when Next tries to invoke Crypto server-side.
Now then, I tell that piece of code to only run if the Window object is available (i.e. I'm in the Browser) and it runs with the native chromium V8 Crypto object.
// import crypto from 'crypto';
export default function Crypto() {
if (typeof window !== 'undefined') {
console.log('CLIENT: ', crypto.randomUUID());
return (
<p>
{crypto.randomUUID()}
</p>
);
}
return (
<h1>SERVER SIDE</h1>
);
}
The only downside is that is somehow still runs twice bc of Next magic, once server side and one client-side, which means it's not bc of React 18. It tells me accurately that which is to be expected as an UUID function always returns a different result.
Browsers restrict access to some crypto APIs when not running in a secure context (as defined here).
Set it to state in a useEffect hook when page initially loads so it persist and then render it from state.
const Crypto = () => {
const [randomUUID, setRandomUUID] = useState();
useEffect((
if (typeof window !== 'undefined' && !randomUUID) {
setRandomUUID(crypto.randomUUID());
}
),[]);
if(!randomUUID) <>No UUID</>;
return <>{randomUUID}</>
}
export default Crypto;
Related
I have trouble loading image(s) as Base64 data. In the following code (partially borrowed from here), the 'loading' function this.textures.addBase64 is used:
import bgSrc from '../assets/img/back.png';
...
create() {
this.numToLoad = 1;
this.textures.on('addtexture', () => {
console.log('another image loaded')
this.numToLoad--;
if (this.numToLoad < 1) this.createNext();
})
this.textures.addBase64('background', bgSrc);
...
}
createNext() {
//...use the images
}
However, the 'addtexture' event never fires. And the image, loaded this way, never loads, no matter how long I wait.
But everything goes fine if I use direct Base64 code, like const bgSrc = 'data:image/png;base64, blablablah' instead of import bgSrc from '../assets/img/back.png'. So the problem is in importing the image.
I use phaser 3 webpack in this project. plus url-loader. Maybe the url-loader is incorrectly set up? I am not sure if the url-loader is even needed for performing npm run start.
Well, it was indeed an incorrect setup of the url-loader. (I used one more plugin in the webpack, and they didn't work well together.)
So, the code above is OK after all.
I was trying to run some tests, but needed test data, so i created a generation file which created dummy html. When I attempt to run it though, it gives me a Reference Error: HTMLDivElement is not defined.
Is there something I need to import such that Node knows what HTMLDivElement is? I am not rendering anything, but just want correct data to pipe into follow on code.
I run my file through TSC, and then run it through node.
Sample Code:
const main = () => {
let root = new HTMLDivElement();
}
main();
Edit:
I was trying to just bypass it with: let root = document.createElement("div"); but node does not understand what document is, so I cant seem to get that running either as a fallback.
Indeed, Node.js doesn't come with a DOM implementation. If you want to run tests that use the DOM, you'll need to either load a Node.js-compatible DOM implementation such as jsdom, or if that doesn't meet your requirements, switch to a browser-based testing environment such as Selenium.
An example of such component is Pikaday (react-pikaday) that contains in its code this line
hasEventListeners = !!window.addEventListener
but window is not defined on server so when I run the code it throws an error ReferenceError: window is not defined.
I tried to solve this issue by conditionally import Pikady component, something along these lines:
let Pikaday;
if (typeof window !== 'undefined' && window.document && window.document.createElement) {
import('./Pikaday').then(P => {
Pikaday = P.default;
});
}
In such a case it doesn't throw an error because the component is not loaded but I presumed that the initial HTML sent from server would not contain this component and then it would somehow "switch" to JS code on client side and the component would load. But this doesn't happen. I'm new to server-side rendering in general so I'm getting a bit lost.
How can I solve this? I have the same problem with Leaflet library that also contains window in its code.
Do a simple check for window.
if (typeof window !== 'undefined') {
// write your logic here
}
I eventually used this fork of Pikaday that introduces several small changes to check for window availability:
https://github.com/everdimension/Pikaday
I don't think there was any other way than edit the plugin itself.
I'm building a project with AdonisJS, and I want to build it as a modular, two-part application: The AdonisJS server runs a control panel, and a custom script outside that server runs an IRC bot. I've been trying to load Lucid into the second script so that I can interface with my database, but it only ever returns an empty object, {}. Some things to note:
I've made sure my database is populated.
I've tested code in my controllers that works and fetches results as expected.
The secondary script boots up all the same parts of Adonis as server.js, sans the actual HTTP server.
I have tried attaching this script to an HTTP server but it made no difference.
I have also tried creating raw QueryBuilder objects with the same results.
Here's the least amount of code I can put together as an example:
#!/usr/bin/node
'use strict'
const fs = require('fs')
const bootstrap = require('./bootstrap/bot')
bootstrap(() =>
{
const AppConfig = use('AppConfig')
const Settings = use('App/Model/Settings')
const get_settings = function * () {
yield Settings.all()
}
console.log(get_settings())
})
console.log() prints {}, even though the same code called within a controller prints all entries from the settings table. bootstrap/bot.js is almost an exact replica of bootstrap/http.js. The only difference is that it doesn't start an HTTP server.
I've scoured the source code looking for things that might happen between starting the server and running controller code to see if there's something critical I'm missing, but I'm lost.
Does anyone know how I can use my Lucid models outside the confines of AdonisJS controllers?
It's because your function is a generator and you can only call a generator with the yield keyword.
So your console.log() should looks like console.log(yield get_settings()).
You may use the package co to create the root generator function.
bootstrap(co(function * () {
// ...
}))
I know window doesn't exist in Node.js, but I'm using React and the same code on both client and server. Any method I use to check if window exists nets me:
Uncaught ReferenceError: window is not defined
How do I get around the fact that I can't do window && window.scroll(0, 0)?
Sawtaytoes has got it. I would run whatever code you have in componentDidMount() and surround it with:
if (typeof(window) !== 'undefined') {
// code here
}
If the window object is still not being created by the time React renders the component, you can always run your code a fraction of a second after the component renders (and the window object has definitely been created by then) so the user can't tell the difference.
if (typeof(window) !== 'undefined') {
var timer = setTimeout(function() {
// code here
}, 200);
}
I would advise against putting state in the setTimeout.
This will settle that issue for you:
typeof(window) === 'undefined'
Even if a variable isn't defined, you can use typeof() to check for it.
This kind of code shouldn't even be running on the server, it should be inside some componentDidMount (see doc) hook, which is only invoke client side. This is because it doesn't make sense to scroll the window server side.
However, if you have to reference to window in a part of your code that really runs both client and server, use global instead (which represents the global scope - e.g. window on the client).
This is a little older but for ES6 style react component classes you can use this class decorator I created as a drop in solution for defining components that should only render on the client side. I like it better than dropping window checks in everywhere.
import { clientOnly } from 'client-component';
#clientOnly
class ComponentThatAccessesWindowThatIsNotSafeForServerRendering extends Component {
render() {
const currentLocation = window.location;
return (
<div>{currentLocation}</div>
)
};
}
https://github.com/peterlazzarino/client-component
<Router onUpdate={() => window.scrollTo(0, 0)} history= {browserHistory}>
if you need to open new page on top in React JS app, use this code in router.js
Move the window and related code to the mounted() lifecycle hook. This is because mounted() hook is called on the client side only and window is available there.