Next.js: How to dynamically import external client-side only React components into Server-Side-Rendering apps developed? - javascript

I know this question has been asked multiple times before but none of the solution seems to work.
I'm trying to use the library 'react-chat-popup' which only renders on client side in a SSR app.(built using next.js framework) The normal way to use this library is to call import {Chat} from 'react-chat-popup' and then render it directly as <Chat/>.
The solution I have found for SSR apps is to check if typedef of window !=== 'undefined' in the componentDidMount method before dynamically importing the library as importing the library normally alone would already cause the window is not defined error. So I found the link https://github.com/zeit/next.js/issues/2940 which suggested the following:
Chat = dynamic(import('react-chat-popup').then(m => {
const {Foo} = m;
Foo.__webpackChunkName = m.__webpackChunkName;
return Foo;
}));
However, my foo object becomes null when I do this. When I print out the m object in the callback, i get {"__webpackChunkName":"react_chat_popup_6445a148970fe64a2d707d15c41abb03"} How do I properly import the library and start using the <Chat/> element in this case?

Next js now has its own way of doing dynamic imports with no SSR.
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
Here is the link of their docs: next js

I've managed to resolve this by first declaring a variable at the top:
let Chat = ''
then doing the import this way in componentDidMount:
async componentDidMount(){
let result = await import('react-chat-popup')
Chat = result.Chat
this.setState({
appIsMounted: true
})
}
and finally render it like this:
<NoSSR>
{this.state.appIsMounted? <Chat/> : null}
</NoSSR>

You may not always want to include a module on server-side. For
example, when the module includes a library that only works in the
browser.
Import the library normally in child component and import that component dynamically on parent component.
https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr
This approach worked for me.

Related

Using react component in a different application

What I'm trying to achieve is:
Building simple react app - the template is create react app
Copying output file (main.*.js)
Pasting it in another react app
Importing render function to render the first app into the second one
Simple react app code:
interface Props {
greeting: string;
}
export module AppModule {
export const sendGreetings = ({ greeting }: Props) => {
return `Hello ${greeting}`;
};
}
Builder file code:
!function(){"use strict";var n;(n||(n={})).sendGreetings=function(n){var e=n.greeting;return"Hello ".concat(e)}}();
Trying to import this file into another app I get this error:
File 'c:/vscode/test-react-app/test-sc-react/src/main.783e0281.js' is not a module.ts(2306)
Which is obvious. I changed the output file manually to:
export function initApp(){"use strict";var n;(n||(n={})).sendGreetings=function(n){var e=n.greeting;return"Hello ".concat(e)}};
It works but the only function that I'm able to access is initApp but not sendGreetings
I've been struggling with this for a while now and I would really appreciate any helpful suggestions
I used Bit.dev for my components that are used across multiple applications & there is an article regarding your issue
https://blog.bitsrc.io/sharing-react-components-across-multiple-applications-a407b5a15186
I think it would help.
🎯 Solution #1
You can use an iframe to inject your react app:
<iframe src='path-to-your-app.html'/>
🎯 Solution #2
Go with micro-frontend architecture approach. Where a front-end app is decomposed into individual, semi-independent "microapps" working loosely together.
As a starting point, you can try npx create-mf-app instead of the CRA.
You can include your js code directly on run time. You can use window.addEventListener to load js/css incoming from an outside source. You just have to append that js to your document on the load event.

Next js named imports with no SSR

I need to convert this line to next.js dynamic import and also without SSR
import { widget } from "./charting_library/charting_library";
I have tried this one
const widget = dynamic(() => import("./charting_library/charting_library").then((mod) => mod.widget), {
ssr: false
});
This seems not the correct way and also charting_libray.js file is a compiled js file in a previous project.
Is the problem is my importing method or the js file? If this is importing method how do I fix this?
const { widget } = await import("./charting_library/charting_library")
Maybe something along those lines might work? As for the SSR side I am not sure if you would need to execute it within a useEffect.

How to force Nextjs to create a chunk for particular module?

I'm trying to use dynamic import for importing a different modules depending on the props passed to the component. Paths to the components are kept in an object
const components = {
'apple': './Apple.js',
'orange': './Orange.js',
// ...
}
Then
// ...
import(components[this.props.type])
// ...
Unfortunately, I get the following error:
Unhandled Rejection (Error): Cannot find module './Apple.js'
It disappears when I explicitly specify the filename (import('./Apple.js')). Is it possible to force nextjs to serve these dynamic modules?
You need to use the dynamic utility from next/dynamic for dynamic import in Next.js. This function is inspired by react-loadable library and uses similar syntax.
The simplest way to dynamically import React component in Next.js is:
const DynamicComponent1 = dynamic(import('../components/hello1'))
If you want to import module that is not a React component than you need to use customizing rendering feature:
const DynamicDeliveryOptions = dynamic({
loader: () => import('../components/delivery-options'),
render(props, loaded) {
const options = loaded.deliveryOptions.map(
option => <option key={option.id}>{option.title}</option>
);
return <select>{options}</select>
},
});
Notice that signature of the render function in Next.js is different from
its signature in react-loadable library (props is the first argument in Next.js).
Returning to your example: I think the best way to import multiple modules will be to declare them all as dynamic modules and conditionally render depending on the passed props (modules won't be loaded before render).
You can tell webpack to put a dynamically named module inside a particular chunk using this syntax:
const module = await import(
/* webpackChunkName: "your_custom_chunk_name" */
"path/to/your/module"
);
Webpack will extract your module's code inside the file: /.next/server/chunks/your_custom_chunk_name.js.
(Tested on NextJs version 12.1.6)

Dynamic Import Node Module With Next.js

I was getting an error of window undefined when using react-leaflet node module because it relies on window and of course SSR does not support window. I found next/dynamic, however, all the examples I found show how to import a component and not a node module. Is it possible to include a node module and if so how? As an example this is what I'm importing that is giving the window undefined error import { Map, TileLayer, Marker } from 'react-leaflet';
The issue is that next.js dynamic import fails on named exports
Looking at the source code of react-leaflet I can see that each named export can be accessed from a particular file e.g. import Map from 'react-leaflet/lib/Map'
Combine it with dynamic import without SSR
const Map = dynamic(() => import('react-leaflet/lib/Map'), {
ssr: false
});
This should do a trick for you.
A better way of getting named exports with a dynamic import is to do this:
const NamedExport = dynamic(() => import('package-name').then((module) => module.NamedExport));
Note: this will chunk the full 'package-name' package and then simply extract the named export. If you are able to path into the module package like the accepted example, that will probably be better as it will likely give you a smaller chunk.
That error happened when you call that dependency's component (Map, TileLayer, Marker) on your component.
Window of undefined occured when your application is being rendered on server side, because window object is belong to the browser.
To avoid error of window of undefined on server side, you can use process.browser in your component.
ref: https://github.com/zeit/next.js/issues/2473?#issuecomment-362119102
I kept hitting type definition issues with the accepted answer, and I didn't want to import the whole library. This is what I came up with:
reexport/index.ts:
export { Marker, /* any other type you'll need */ } from 'react-leaflet';
Then, in your component/page, simply import anything from the module above.
const Marker = useMemo(() => dynamic<MarkerProps>(
() => import('./reexport').then(re => re.Marker),
{ ssr: false }
), []);
Note that I've wrapped the call to dynamic in a useMemo hook, and also provided a type for the props.

Code Splitting React components

Im relatively new to React (i'm using preact.js, to be precise) and i am trying to code split out react components using webpack 2.
Im exporting my component as stated in the documentation and i am then importing it on load.
import('./components/List').then((List) => {
render(<List />, document.getElementById('main'));
});
The script loads but i'm not handling the promise callback correctly and finding it hard to see any documentation that shows a working version.
List returns the following object:
I saw your repo. It looks like that your list component doesn't have a export default.
I would add the default and inside your then, when you handle the promise, I'd do it in this way
.then(module => {
const Component = module.default;
render(<Component />, document.getElementById('main'))
})

Categories