Trying to create an xterm react component in Next.js I got stuck as I'm not able to get over an error message I've never got before.
I'm trying to import a npm client-side module called xterm, but if I add the import line the application crashes.
import { Terminal } from 'xterm'
The error reads Server Error... ReferenceError: self is not defined
and then shows this chunk of code as Source
module.exports = require("xterm");
According to some research I did, this has to do with Webpack and could be helped if something like this was done:
output: {
globalObject: 'this'
}
Would you know how to fix this?
The error occurs because the library requires Web APIs to work, which are not available when Next.js pre-renders the page on the server-side.
In your case, xterm tries to access the window object which is not present on the server. To fix it, you have to dynamically import xterm so it only gets loaded on the client-side.
There are a couple of ways to achieve this in Next.js.
#1 Using dynamic import()
Move the import to your component's useEffect, then dynamically import the library and add your logic there.
useEffect(() => {
const initTerminal = async () => {
const { Terminal } = await import('xterm')
const term = new Terminal()
// Add logic with `term`
}
initTerminal()
}, [])
#2 Using next/dynamic with ssr: false
Create a component where you add the xterm logic.
// components/terminal-component
import { Terminal } from 'xterm'
function TerminalComponent() {
const term = new Terminal()
// Add logic around `term`
return <></>
}
export default TerminalComponent
Then dynamically import that component when using it.
import dynamic from 'next/dynamic'
const TerminalComponent = dynamic(() => import('<path-to>/components/terminal-component'), {
ssr: false
})
As an alternative, you could add the logic directly when dynamically importing the library with next/dynamic to avoid having an extra file for it.
import dynamic from 'next/dynamic'
const Terminal = dynamic(
{
loader: () => import('xterm').then((mod) => mod.Terminal),
render: (props, Terminal) => {
const term = new Terminal()
// Add logic with `term`
return <></>
}
},
{
ssr: false
}
)
Related
Trying to create an xterm react component in Next.js I got stuck as I'm not able to get over an error message I've never got before.
I'm trying to import a npm client-side module called xterm, but if I add the import line the application crashes.
import { Terminal } from 'xterm'
The error reads Server Error... ReferenceError: self is not defined
and then shows this chunk of code as Source
module.exports = require("xterm");
According to some research I did, this has to do with Webpack and could be helped if something like this was done:
output: {
globalObject: 'this'
}
Would you know how to fix this?
The error occurs because the library requires Web APIs to work, which are not available when Next.js pre-renders the page on the server-side.
In your case, xterm tries to access the window object which is not present on the server. To fix it, you have to dynamically import xterm so it only gets loaded on the client-side.
There are a couple of ways to achieve this in Next.js.
#1 Using dynamic import()
Move the import to your component's useEffect, then dynamically import the library and add your logic there.
useEffect(() => {
const initTerminal = async () => {
const { Terminal } = await import('xterm')
const term = new Terminal()
// Add logic with `term`
}
initTerminal()
}, [])
#2 Using next/dynamic with ssr: false
Create a component where you add the xterm logic.
// components/terminal-component
import { Terminal } from 'xterm'
function TerminalComponent() {
const term = new Terminal()
// Add logic around `term`
return <></>
}
export default TerminalComponent
Then dynamically import that component when using it.
import dynamic from 'next/dynamic'
const TerminalComponent = dynamic(() => import('<path-to>/components/terminal-component'), {
ssr: false
})
As an alternative, you could add the logic directly when dynamically importing the library with next/dynamic to avoid having an extra file for it.
import dynamic from 'next/dynamic'
const Terminal = dynamic(
{
loader: () => import('xterm').then((mod) => mod.Terminal),
render: (props, Terminal) => {
const term = new Terminal()
// Add logic with `term`
return <></>
}
},
{
ssr: false
}
)
Trying to create an xterm react component in Next.js I got stuck as I'm not able to get over an error message I've never got before.
I'm trying to import a npm client-side module called xterm, but if I add the import line the application crashes.
import { Terminal } from 'xterm'
The error reads Server Error... ReferenceError: self is not defined
and then shows this chunk of code as Source
module.exports = require("xterm");
According to some research I did, this has to do with Webpack and could be helped if something like this was done:
output: {
globalObject: 'this'
}
Would you know how to fix this?
The error occurs because the library requires Web APIs to work, which are not available when Next.js pre-renders the page on the server-side.
In your case, xterm tries to access the window object which is not present on the server. To fix it, you have to dynamically import xterm so it only gets loaded on the client-side.
There are a couple of ways to achieve this in Next.js.
#1 Using dynamic import()
Move the import to your component's useEffect, then dynamically import the library and add your logic there.
useEffect(() => {
const initTerminal = async () => {
const { Terminal } = await import('xterm')
const term = new Terminal()
// Add logic with `term`
}
initTerminal()
}, [])
#2 Using next/dynamic with ssr: false
Create a component where you add the xterm logic.
// components/terminal-component
import { Terminal } from 'xterm'
function TerminalComponent() {
const term = new Terminal()
// Add logic around `term`
return <></>
}
export default TerminalComponent
Then dynamically import that component when using it.
import dynamic from 'next/dynamic'
const TerminalComponent = dynamic(() => import('<path-to>/components/terminal-component'), {
ssr: false
})
As an alternative, you could add the logic directly when dynamically importing the library with next/dynamic to avoid having an extra file for it.
import dynamic from 'next/dynamic'
const Terminal = dynamic(
{
loader: () => import('xterm').then((mod) => mod.Terminal),
render: (props, Terminal) => {
const term = new Terminal()
// Add logic with `term`
return <></>
}
},
{
ssr: false
}
)
I want to write stories for both React and Svelte components. I already have a few React components, and I'm attempting to install Svelte. My closest attempt can either run React OR Svelte depending on whether I comment out my React configuration. If I don't comment it out, I get this message when I look at my Svelte component in storybook:
Error: Objects are not valid as a React child (found: object with keys {Component}). If you meant to render a collection of children, use an array instead.
in unboundStoryFn
in ErrorBoundary
(further stack trace)
This refers to my story stories/test.svelte-stories.js:
import { storiesOf } from '#storybook/svelte';
import TestSvelteComponent from '../src/testComponentGroup/TestSvelteComponent.svelte';
storiesOf('TestSvelteComponent', module)
.add('Svelte Test', () => ({
Component: TestSvelteComponent
}));
My configuration is as follows:
.storybook/config.js:
import './config.react'; // If I comment out this line, I can make the svelte component work in storybook, but of course my react stories won't appear.
import './config.svelte';
.storybook/config.react.js:
import { configure } from '#storybook/react';
const req = require.context('../stories', true, /\.react-stories\.js$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);
.storybook/config.svelte.js:
import { configure } from '#storybook/svelte';
const req = require.context('../stories', true, /\.svelte-stories\.js$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);
.storybook/webpack.config.js:
module.exports = async ({ config, mode }) => {
let j;
// Find svelteloader from the webpack config
const svelteloader = config.module.rules.find((r, i) => {
if (r.loader && r.loader.includes('svelte-loader')) {
j = i;
return true;
}
});
// safely inject preprocess into the config
config.module.rules[j] = {
...svelteloader,
options: {
...svelteloader.options,
}
}
// return the overridden config
return config;
}
src/testComponentGroup/TestSvelteComponent.svelte:
<h1>
Hello
</h1>
It seems as though it's attempting to parse JSX via the Svelte test files, but if I import both React AND Svelte configurations I can still see the React components behaving properly.
See this discussion on github : https://github.com/storybookjs/storybook/issues/3889
It's not possible now and it's planned for the v7.0
The official position now is to create two sets of configuration (preview and manager), instanciate two separates storybook, and then use composition to assemble the two storybook into one.
I'm trying to build a React web app (v 16.13.0). I want a flash message component, to display status after a form is submitted. If there is already something standard, that would be preferable, but since I can't find anything, I'm trying to roll my own using this -- https://medium.com/#jaouad_45834/building-a-flash-message-component-with-react-js-6288da386d53 . Here's the component
import React, { useEffect, useState } from 'react';
import Bus from '../Utils/Bus';
import './index.css';
export const Flash = () => {
let [visibility, setVisibility] = useState(false);
let [message, setMessage] = useState('');
let [type, setType] = useState('');
useEffect(() => {
Bus.addListener('flash', ({message, type}) => {
setVisibility(true);
setMessage(message);
setType(type);
setTimeout(() => {
setVisibility(false);
}, 4000);
});
}, []);
useEffect(() => {
if(document.querySelector('.close') !== null) {
document.
querySelector('.close').
addEventListener('click', () => setVisibility(false));
}
})
return (
visibility && <div className={`alert alert-${type}`}>
<span className="close"><strong>X</strong></span>
<p>{message}</p>
</div>
)
}
Problem is, web site uses custom code, but doesn't show source for
Bus.addListener('flash', ({message, type}) => {
setVisibility(true);
setMessage(message);
setType(type);
setTimeout(() => {
setVisibility(false);
}, 4000);
});
so my question is, how do I add an event listener to a React component?
Edit: In response to the answer given, I created this file ...
localhost:client davea$ cat src/Utils/Bus.js
import EventEmitter from 'events';
export default new EventEmitter();
but re-compiling my module results in this error ...
./src/components/Flash/index.js
Module not found: Can't resolve '../Utils/Bus' in '/Users/davea/Documents/workspace/chicommons/maps/client/src/components/Flash'
These are the first lines of the file above. Note the second "import" where I'm importing "Bus" ...
import React, { useEffect, useState } from 'react';
import Bus from '../Utils/Bus';
import './index.css';
export const Flash = () => {
The website included the code: https://medium.com/#jaouad_45834/building-a-flash-message-component-with-react-js-6288da386d53
To set that up, we need to create a new folder in our root directory and name it Utils and create on it a Bus.js file with will contains the following code:
import EventEmitter from 'events';
export default new EventEmitter();
This is all Bus.js is, a simple event emitter.
You can also use react-toastify or useToasts for this.
In order to show the flash message, you need to do something like the following.
Bus.emit('flash', {type: 'danger', message: 'error occurred'});
I have used the code you have provided and mixed it with a dummy form. Upon submitting the form, the flash message appears successfully.
Live example & working code is here:
https://codesandbox.io/s/dazzling-lamarr-k3cn5
Some notes:
I have refactored and removed 2nd useEffect as it is inefficient and unnecessary. The onClick can very well be applied to the close-span-element
If you are using redux, you can use create global Flash/Alert message and connect it to the store. Any redux-connected-component can simply work its own logic and dispatch an alert action and render different types of messages.
Using ready made libraries are also cool.
I think what you want is widely known as a Toaster, and you can use a library like React Toastify [https://github.com/fkhadra/react-toastify] with a simple configuration and high customization
I have a path.json file that contains the path of a component
// path.json
{
"main": "./login/index.js",
"paths": [
{
"name": "login",
"path": "./login/index.js",
"image": ""
}
]
}
I want to load './login/index.js' file dynamically in react native and render this particular file
My current implementation
const MyComponent = createLazyContainer(() => {
const componentPath = PathJson.main; // ./login/index.js
return import(`${componentPath}`); //import error here # line 7
});
export default MyComponent;
I am getting following error :
Invalid call at line 7: import("" + componentPath)
What people have been telling you in the thread is correct but I'd like to add one possible solution. All the imports/require are resolved at compilation time and not at running time which you are trying to do. By the time you are running your app, if you haven't imported the files, you can't use them.
There is a workaround tho, assuming that you know all the files that you might in advance which is to do something like a factory:
const possiblePaths = {
'one': require('path/to/file/1'),
'two': require('path/to/file/2')
}
function(type){
return possiblePaths[type];
}
And then you use it somehow like:
render(){
const MyComponent = function('one');
return <MyComponent/>;
}
This is more or less pseudo code and my not work right away, but hopefully yo get the idea. You need to store a reference to each of the imports you might need and then dont use the import, use the reference that was created for you at compilation time.
Actually, the React Native development concerns are not like development for the Web.
Just for this reason, it is not so important at all to have lazy loading in the production of a react-native project. Just import anything you want and then use them in any files of the project. all of them are in the bundle of production and exactly it is not important at all.
So for this problem, I prefer to have a helper file to collect all selectable libraries and export them:
// helper file
export { default as Index } from './Login';
export { default as OtherComponent } from './OtherComponent';
Then when you wanna use:
import { Index, OtherComponent } from 'helper';
~~~
render() {
const MyComponent = someCondition ? Index : OtherComponent;
return (
<MyComponent />;
);
}
Solution:
const allPaths = {
path1: require('file path1').default,
path2: require('file path2').default
};
render(){
const MyComponent = allPaths["path1"];
return <MyComponent/>
}
In React Native all the files that are being imported are bundled together, only those files can be dynamically imported.
Let's say you have three files index.js, test_1.js and test_2.js and if you have imported only test_1.js in index.js than React Native will only bundle those two files leaving test_2.js.
So to answer your question even if dynamic import works in React Native but because these files are not part of the bundle you are not able to import them.
I've once been in a similar situation where I need to do imports by variable, but that is limited to importing components inside a component and it uses code-splitting (Edit: I'm playing around to look for a solution without relying on code-splitting, I just realized there was a react-native tag in the question, and I don't think code-splitting is a good choice to go with in RN). I'm not sure by how much my method could help you, but here goes.
Side notes:
Importing folder that contains an index.js(jsx|ts|tsx) file should automatically resolve to that index file.
Importing from from './login/index.js' usually throws a 'Module not found' error. Either import from './login/index' or from './login but I prefer the last one as it's the shortest & simplest.
In path.json:
{
"main": "./login", // '.js' is removed
"paths": [
{
"name": "login",
"path": "./login/index.js", // Not sure what this is for, but if necessary, remove the '.js' here as well
"image": ""
}
]
}
In MyComponent.js:
import React, { lazy, Suspense } from 'react'
import PathJson from './path'
// 1. We need a UI to show while component is being loaded
const Loader = () => <div>{'Loading...'}</div>
// 2. We need a fallback UI if component is not found
const DocUnavailable = () => <div>{'We\'re sorry, but this document is unavailable.'}</div>
// 3. Create a resolver function ('resolver' is just a name I give)
function resolveImport(pathToComponent, FallbackComponent) {
let componentFound = false
let RenderComponent = () => <FallbackComponent /> // Assign fallback first
try {
if (require.resolve(pathToComponent)) {
componentFound = true
}
} catch (e) { } // Kinda hacky, if you don't mind, but it works
if (componentFound) {
// If found, replace fallback with the valid component
RenderComponent = lazy(() => import(pathToComponent))
}
return RenderComponent
}
// 4. Finally, implement it in a component
class MyComponent extends React.Component {
render() {
const componentPath = PathJson.main
const RenderComponent = resolveImport(componentPath, DocUnavailable)
return (
<Suspense fallback={<Loader />}>
<RenderComponent />
</Suspense>
)
}
}
export default MyComponent
References:
Implementation for 'resolver' function based on Langutil
Code-splitting with lazy & Suspense based on React Docs