I have a backend app that constantly serves events to my React app via Web Sockets. When a specific event is received a new browser tab should be opened.
The application will be run by a user in multiple tabs, so I need to open a new tab only once and prevent it from being opened by all running instances.
I've tried using Redux persistent storage, but it doesn't seem to correspond my needs. The best solution that I've found is Shared Workers.
I've tried using Shared Worker in my React app, but I can't set up it properly. It's either being imported incorrectly or Webpack is unable to load it
Uncaught SyntaxError: Unexpected token <
When I googled I haven't found any examples of using Shared Worker in React app (with or without CRA) and at this point, I'm not even sure it's possible. I did found some Web Workers examples, but they have totally different configs.
Can anyone please share some specifics of running Shared Worker in React? Or any other ideas that can provide me with similar functionality will be also greatly appreciated.
Edit: Adding lastest code of what I've tried. Disregard the counter logic, consider just the setup:
worker.js
import React from 'react';
export const startCounter = () => {
window.self.addEventListener("message", event => {
console.log(event.data, self);
let initial = event.data;
setInterval(() => this.postMessage(initial++), 1000);});
}
App.js
import React, { Component } from 'react';
import {startCounter} from './worker';
class App extends Component {
componentDidMount() {
const worker = new SharedWorker(startCounter);
worker.port.start()
// worker.postMessage(this.state.counter);
// worker.addEventListener('message', event => this.setState({counter: event.data}));
}
render() {
return (
<div className="App">
</div>
);
}
}
export default App;
make a file called WebWorker.js which looks like this:
export default class WebWorker {
constructor(worker) {
const code = worker.toString();
const blob = new Blob(['('+code+')()']);
return new SharedWorker(URL.createObjectURL(blob));
}
}
and import it to your main file and do this:
const workers = new WebWorker(worker);
workers.postMessage(some message);
Clarifying #Birat's answer: The SharedWorker constructor is looking for a URL, but here you're passing it a function:
const worker = new SharedWorker(startCounter);
Give this a try instead:
const worker = new SharedWorker(new URL('./worker', import.meta.url));
Related
I've been using the solution at NextJS deploy to a specific URL path (and Deploy your NextJS Application on a different base path (i.e. not root)
) for a year now on our project. Our next.js server is hosted on another website under a sub-path, but the sub-path comes from the other server, not ours. I.e. we're at http://example.com/project/path. But our server is running at http://localhost:8081/ so we can't use the basePath since that changes our local path. This has been working great. We use a wrapped next/link for our links and just prepend our own basePath to API calls in most cases.
I recently tried switching to use getServerSideProps() rather than getInitialProps() since I was reading online that is the new preferred way (a single API call rather than the multiple we were making before). The problem I'm having is that I can't find a way to tell next.js that it needs to add the basePath to the getServerSideProps API calls it makes. We are using assetPrefix to point our images and css at the sub-path but it looks like there was a bug https://github.com/vercel/next.js/issues/15563 which was fixed that let us use assetPrefix rather than basePath.
Again, we can't use basePath since it changes localhost to http://localhost/project/path and it's the external website that has the extra pathing. Is there any other solution? Or are we just stuck on getIntialProps since it works and hope we don't lose that option in the future?
Currently, server-side rendering works, but when I navigate client-side, it returns a 404 on the getServerSideProps API call and the browser than redirects and loads the page server-side like:
Chrome network load. While it "works", it's not very performant since it must server-side render every page.
Edit: The proposed solution https://stackoverflow.com/a/66642225/1870780 does not work since we cannot use a basePath since it changes the local server base, not a remote server path pointing to our server.
Thank you #juliomalves for pointing me in the direction of the solution. The discussion at https://github.com/vercel/next.js/discussions/25681#discussioncomment-2026813 has a potential solution which works. In-lining the solution here to save others time, but credit goes to alizeait for the solution. Main difference between my solution an his, is his namespace does not include a leading / while mine does.
Typescript solution:
import { AppProps } from "next/app";
import { Router } from "next/router";
import { useEffect } from "react";
const useInterceptNextDataHref = ({
router,
namespace
}: {
router: Router;
namespace: string;
}) => {
useEffect(() => {
if (router.pageLoader?.getDataHref) {
const originalGetDataHref = router.pageLoader.getDataHref;
router.pageLoader.getDataHref = function (args: {
href: string;
asPath: string;
ssg?: boolean;
rsc?: boolean;
locale?: string | false;
}) {
const r = originalGetDataHref.call(router.pageLoader, args);
return r && r.startsWith("/_next/data")
? `${namespace}${r}`
: r;
};
}
}, [router, namespace]);
};
export default function MyApp ({ Component, pageProps, router }: AppProps) {
useInterceptNextDataHref({
router,
namespace: "/my-base-path"
});
return <Component {...pageProps} />;
}
I'd like to import this javascript package in React
<script src="https://cdn.dwolla.com/1/dwolla.js"></script>
However, there is no NPM package, so I can't import it as such:
import dwolla from 'dwolla'
or
import dwolla from 'https://cdn.dwolla.com/1/dwolla.js'
so whenver I try
dwolla.configure(...)
I get an error saying that dwolla is undefined. How do I solve this?
Thanks
Go to the index.html file and import the script
<script src="https://cdn.dwolla.com/1/dwolla.js"></script>
Then, in the file where dwolla is being imported, set it to a variable
const dwolla = window.dwolla;
This question is getting older, but I found a nice way to approach this using the react-helmet library which I feel is more idiomatic to the way React works. I used it today to solve a problem similar to your Dwolla question:
import React from "react";
import Helmet from "react-helmet";
export class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
myExternalLib: null
};
this.handleScriptInject = this.handleScriptInject.bind(this);
}
handleScriptInject({ scriptTags }) {
if (scriptTags) {
const scriptTag = scriptTags[0];
scriptTag.onload = () => {
// I don't really like referencing window.
console.log(`myExternalLib loaded!`, window.myExternalLib);
this.setState({
myExternalLib: window.myExternalLib
});
};
}
}
render() {
return (<div>
{/* Load the myExternalLib.js library. */}
<Helmet
script={[{ src: "https://someexternaldomain.com/myExternalLib.js" }]}
// Helmet doesn't support `onload` in script objects so we have to hack in our own
onChangeClientState={(newState, addedTags) => this.handleScriptInject(addedTags)}
/>
<div>
{this.state.myExternalLib !== null
? "We can display any UI/whatever depending on myExternalLib without worrying about null references and race conditions."
: "myExternalLib is loading..."}
</div>
</div>);
}
}
The use of this.state means that React will automatically be watching the value of myExternalLib and update the DOM appropriately.
Credit: https://github.com/nfl/react-helmet/issues/146#issuecomment-271552211
for typescript developers
const newWindowObject = window as any; // cast it with any type
let pushNotification = newWindowObject.OneSignal; // now OneSignal object will be accessible in typescript without error
You can't require or import modules from a URL.
ES6: import module from URL
What you can do is make an HTTP request to get the script content & execute it, as in the answer for how to require from URL in Node.js
But this would be a bad solution since your code compilation would depend on an external HTTP call.
A good solution would be to download the file into your codebase and import it from there.
You could commit the file to git if the file doesn't change much & are allowed to do it. Otherwise, a build step could download the file.
var _loaded = {};
function addScript(url) {
if (!loaded[url]) {
var s = document.createElement('script');
s.src = url;
document.head.appendChild(s);
_loaded[url] = true;
}
}
how to load javascript file from cdn server in a component
Add the script tag in your index.html and if you are using Webpack, you can use this webpack plugin https://webpack.js.org/plugins/provide-plugin/
I am trying to run a face detection process in the background of my React app using a web worker. I am using a library called face-api.js to do the face detection. But I'm unsure as to how to import this library to use in my worker.js file.
worker.js
import * as faceapi from 'face-api.js';
this.onmessage = (e) => {
console.log("Message from worker.js");
this.postMessage('Hello Main')
};
TestComponent.js
import React, {useEffect} from 'react'
function TestComponent() {
useEffect(() => {
const worker = new Worker('./workers/worker.js')
worker.postMessage('Hello Worker')
worker.onmessage = e => {
console.log('Message received from worker:', e.data)
}
}
);
return (
<div>
<h1> Test Component </h1>
</div>
)
}
export default TestComponent
I run this test code and I get the following error on chrome:
/workers/worker.js:1 Uncaught SyntaxError: Cannot use import statement outside a module
I have tried using require to import the library which didn't work. I also tried declaring the worker as a module
const worker = new Worker('./workers/worker.js', type: "module")
This makes the error go away but then nothing works: the worker does not do what it is intended to do.
Please point me in the right direction. Thanks in advance!
I guess you should use importScript() in your service worker file
you need to use {type:"module"} in the options of worker constructor for use "import" features in the worker file.
https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
options Optional
An object containing option properties that can be set when creating the object instance. Available properties are as follows:
type: A DOMString specifying the type of worker to create. The value can be classic or module. If not specified, the default used is classic.
credentials: A DOMString specifying the type of credentials to use for the worker. The value can be omit, same-origin, or include. If not specified, or if type is classic, the default used is omit (no credentials required).
name: A DOMString specifying an identifying name for the DedicatedWorkerGlobalScope representing the scope of the worker, which is mainly useful for debugging purposes.
I am trying to get the browser fingerprint from react application using clientJS. When I import clientJS module and try to access get fingerprint function, it's throwing error saying Get Finger Print is not a function. But If i create a clientJS instance from window.ClientJS(). I am able to generate the fingerprint. Why am i not able get the fingerprint function from instance of ClientJS?
Not working
import ClientJS from "clientjs";
const client = new ClientJS()
const fingerPrint = client.getFingerprint()
console.log(client)
Working
import ClientJS from "clientjs";
const client = new ClientJS()
const windowClient = new window.ClientJS();
const fingerPrint = windowClient.getFingerprint()
console.log(windowClient);
I haven't done much with client Js but I have a tiny package I built that you can use to get a browser fingerprint with ease.
You can use it in any of your frontend application.
It is available here https://github.com/successtar/browser-signature
This worked for me in a react application
import ClientJS from 'clientjs'
import { useState, useEffect } from 'react'
let clientJs: any
const MyComponent = () => {
const [fingerprint, setFingerprint] = useState('')
useEffect(() => {
clientJs = new ClientJS()
}, []) // initiate on the first load
setFingerprint(clientJs.getFingerprint)
}
Code below is working for me in React.
import ClientJS from 'clientjs';
var client = new ClientJS();
client.getBrowser()
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.