importScripts in Web Workers is undefined inside a React/Webpack environment - reactjs

I am working on a React app created with create-react-app. I was having trouble creating a web worker in it so I posted a question here on SO: Creating a web worker inside React
I've found a solution, as written in the post above, to load a worker without ejecting the app and messing with the Webpack config. This is the code, from the post above:
// worker.js
const workercode = () => {
self.onmessage = function(e) {
console.log('Message received from main script');
const workerResult = 'Received from main: ' + (e.data);
console.log('Posting message back to main script');
self.postMessage(workerResult);
}
};
let code = workercode.toString();
code = code.substring(code.indexOf("{")+1, code.lastIndexOf("}"));
const blob = new Blob([code], {type: "application/javascript"});
const worker_script = URL.createObjectURL(blob);
module.exports = worker_script;
and in the file that uses the worker:
import worker_script from './worker';
const myWorker = new Worker(worker_script);
myWorker.onmessage = (m) => {
console.log("msg from worker: ", m.data);
};
myWorker.postMessage('im from main');
It works, however, I cannot seem to get importScripts to work. Even if I do this (outside onmessage or inside onmessage):
if (typeof importScripts === 'function') {
importScripts('myscript.js');
}
In that case, the if statement turns out to be true, but then fails on the actual import with the same error message 'importScripts' is not defined as if the if statement is a false positive, which doesn't sound right. I'd say this is a context issue and that the worker probably isn't loading properly (although it seems to work), but it's just a guess.
Any ideas what's happening here?

importScripts in a worker created from Blob works fine, at least in 2021 (react 17.0.2, react-scripts 4.0.3, Chrome 92). The imported script URL must be absolute because worker was created from Blob.
The original issue might have been a bug in webpack or the transpilation might have changed the code in a weird way.
const workercode = () => {
importScripts("https://example.com/extra.js");
console.log(self.extraValue); // 10
self.onmessage = function(e) {
console.log('Message received from main script');
...
}
};
 
// extra.js
self.extraValue = 10;

Looks like this is still broken in 2022 - Seems there is a regression coming down the dev pipeline (at least in Android WebView and possibly some dev/canary chrome verions.)
https://bugs.chromium.org/p/chromium/issues/detail?id=1078821

Related

I am using React Native Expo And need to do live background location tracking

React Native Expo: Background Location has not been configured. To enable it, add location to UIBackgroundModes in Info.plistfile
I am using expo-location and trying to do background location tracking.
my app.json under IOS includes this
"infoPlist": { "UIBackgroundModes": [ "location", "fetch" ],
I am calling the startLocationUpdatesAsync function as such
` useEffect(() => {
var appLocation = undefined;
const startLocationTracking = async () => {
var opts = {
accuracy: Location.Accuracy.BestForNavigation,
distanceInterval: 1, //meters
};
appLocation = await Location.watchPositionAsync(opts, (location) => {
dispatch(setCurrentLocation(location.coords));
sendLocationToAPI(location);
});
const backgroundLocation = await Location.startLocationUpdatesAsync("background-location")
}`
I end up getting this error
Unhandled promise rejection: Error: Background Location has not been configured. To enable it, add locationtoUIBackgroundModes in Info.plist file.
I Do not know how to get rid of this, but I think I may have to expo eject and switch to cli. If anyone can help so I can avoid this it would be really helpful.

Firebase production Error: ReferenceError: emptyChildrenSingleton is not defined

I have a nextjs project, v12.1.3 with react v18.1.0. I use firebase realtime database to handle notifications into my project. It works in develop mode, but it doesn't in production (on built environment).
The errors I get are the following:
ReferenceError: emptyChildrenSingleton is not defined
at new bV (a198fdd9-b0625f5d1b77c03a.js:1:75705)
at new cF (a198fdd9-b0625f5d1b77c03a.js:1:95593)
at a198fdd9-b0625f5d1b77c03a.js:1:124619
at get _repo [as _repo] (a198fdd9-b0625f5d1b77c03a.js:1:124994)
at get _root [as _root] (a198fdd9-b0625f5d1b77c03a.js:1:125199)
at d_ (a198fdd9-b0625f5d1b77c03a.js:1:120499)
at n (index-493c2cb0fc06fe87.js:1:879)
at index-493c2cb0fc06fe87.js:1:826
at g0 (framework-47484f2290a3befd.js:1:91915)
at h9 (framework-47484f2290a3befd.js:1:113308)
[2022-06-13T21:12:40.922Z] #firebase/database: FIREBASE WARNING: Exception was thrown by user callback. TypeError: Cannot read properties of undefined (reading 'syncPointTree_')
at cM (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:99754)
at cI (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:97044)
at dC (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:109983)
at http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:124197
at ap (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:3775)
at d.onServerInfoUpdate_ (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:124182)
at d.handleTimestamp_ (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:43294)
at d.onReady_ (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:39829)
at aM.onConnectionEstablished_ (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:26211)
at aM.onHandshake_ (http://localhost:3000/_next/static/chunks/a198fdd9-b0625f5d1b77c03a.js:1:25266)
Uncaught TypeError: Cannot read properties of undefined (reading 'syncPointTree_')
at cM (a198fdd9-b0625f5d1b77c03a.js:1:99754)
at cI (a198fdd9-b0625f5d1b77c03a.js:1:97044)
at dC (a198fdd9-b0625f5d1b77c03a.js:1:109983)
at a198fdd9-b0625f5d1b77c03a.js:1:124197
at ap (a198fdd9-b0625f5d1b77c03a.js:1:3775)
at d.onServerInfoUpdate_ (a198fdd9-b0625f5d1b77c03a.js:1:124182)
at d.handleTimestamp_ (a198fdd9-b0625f5d1b77c03a.js:1:43294)
at d.onReady_ (a198fdd9-b0625f5d1b77c03a.js:1:39829)
at aM.onConnectionEstablished_ (a198fdd9-b0625f5d1b77c03a.js:1:26211)
at aM.onHandshake_ (a198fdd9-b0625f5d1b77c03a.js:1:25266)
The integration is very basic: I write my notification client side and create them server side with firebase-admin. So the problem is reading from realtime database (in prod only).
The code is something like this (i simplified it)
const [textData, setTextData] = useState<any[]>([]);
useEffect(() => {
initFirebase();
fetchData();
}, []);
const fetchData = () => {
const db = getDatabase();
const starCountRef = ref(db, 'testData');
onValue(starCountRef, (snapshot) => {
const data = snapshot.val();
setTextData(
Object.entries(data || {}).map(
([key, value]: [key: string, value: any]) => ({
key,
text: value?.text,
})
)
);
});
};
Where initFirebase() is a classic:
import { getApps, initializeApp } from 'firebase/app';
const firebaseConfig = {
//my configs
};
// Initialize Firebase
const initFirebase = () => {
if (getApps().length && getApps.length > 0) return getApps()[0];
return initializeApp(firebaseConfig);
};
export default initFirebase;
It might seem all right. And it is. I made this into an empty project and it actually work. (here the repo without the error).
I tried to replicate the error I have in my repo and I did it. This is the repo with the error. The error must be something with the packages, react or nextjs version, but I don't get it.
For simplicity You can take a look directly to the repos. I think the code is all right.
How do you think this error can be fixed?
P.S. the repos are also deployed on vercel, so you can try also to see the logs. Of course you can also make a PR.
I found out the problem is the last version of next (12.1.3).
In particular swcMinify. In fact, I had this option set to true into the file next.config.js. I also found out that there are several issues about swcMinify in production build. So, in order to solve the problem I described above, you just have to turn off swcMinify, or remove it. After that everything seems to be all right.
Conclusion: This is not a firebase error.

Unit Tests Failing After Updating React Scripts from v.3.4.4 to v.4.0.3

I'm working on a new project for a client and have been asked to update create-react-app (react-scripts) from v.2.0.5 to v.4.0.3. I did that, and a bunch of unit tests failed. I went back through the project and isolated the breaking change to be the update from react-scripts 3.4.4 to 4.0.0.
Basically, the main error I'm seeing seems to apply to any tests running against async/await methods. Jest reports that the test times out but these tests are only timing out because they are failing. They all pass without issue on an earlier version of react-scripts (and by extension an earlier version of Jest I'd guess).
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
The timeout seems to be relating to an issue with promises as it's preceeded by this error:
TypeError: Cannot read property 'then' of undefined
Along with a fairly indecipherable stack trace that lists a load of node modules.
Here's an example of the test that's failing:
test('dispatching fetchPublishedArticlesAuthors action causes an API GET and updates the store', async (done) => {
const options = { page: 0, size: 20, sort: 'createdBy.firstName,asc' };
store.dispatch(fetchPublishedArticlesAuthors(options));
const state = await stateChange(store);
expect(fetch).toHaveBeenCalledWith(
`https://content.onehub.test/articles/authors?page=0&size=20&sort=createdBy.firstName%2Casc`,
expect.objectContaining({ method: 'GET' }),
);
expect(getPublishedArticlesAuthors(state)).toMatchSnapshot();
done();
});
The await stateChange(store) references this method:
export function stateChange(store) {
return new Promise((resolve) => {
let currentState = store.getState();
store.subscribe(() => {
const newState = store.getState();
if (newState !== currentState) {
resolve(newState);
}
currentState = newState;
});
});
}
This method works without issue on another project running react-scripts 4.0.3 so I don't think that it's the issue per se but it is failing to return anything if I'm not mistaken.
Store is mocked as follows:
beforeEach(() => {
store = mockStore({ initialState, reducers, rootSaga: sagas });
fetch.mockClear();
});
Any tips on where to start? Confused as to why it fails on a more recent version but passes on an earlier one.
The solution was to set resetMocks to false. I tried doing this in Jest config but couldn't get it working so just added it directly to package.json:
"jest": {
"resetMocks": false
}
It wasn't obvious from the failing tests but having read the release notes you can see that the version of Jest used by Create React App changed from v24 to v26. In the process, resetMocks is set to true which was breaking the implementation in the test above, causing them all to time out.

Precached content does not load from cache

In my React SSR application I have implemented service worker(via Workbox).
It's working fine. Every time when I am changing some piece of code, rebuilding again, running the server, going to the browser, I am seeing that my cache was updated succesfully.
But one thing I cant understand. When I am deleting some asset(js or css) from my local server and trying to do some action in the browser(which invokes that asset) I am getting a chunk error, which says that the file is not available.
The main question is if that asset is already is in cache storage it should not be loaded from that cache or I have missed something?
The components I have used is
Node/express(for server)
#loadable/components(for code splitting), combined with webpack
Google workbox plugin
// my sw.js file
import { skipWaiting } from 'workbox-core';
import { precacheAndRoute } from 'workbox-precaching';
declare const self: Window & ServiceWorkerGlobalScope;
precacheAndRoute(self.__WB_MANIFEST);
skipWaiting();
// my workbox setup
const serviceWorkerRegistration = async (): Promise<void> => {
const { Workbox } = await import('workbox-window');
const wb = new Workbox('./service-worker.js');
wb.addEventListener('activated', (event: any) => {
if (event.isExternal) {
window.location.reload();
}
});
wb.register();
};
This is due to your usage of skipWaiting() inside of your service worker. When the waiting service worker activates, it will delete all of the outdated precached entries that are no longer associated with the new service worker deployment.
There is more background information in this two closely related answers, as well as a presentation:
What are the downsides to using skipWaiting and clientsClaim with Workbox?
Workbox: the danger of self.skipWaiting()
Paying Attention while Loading Lazily

Webview context doesn't show up anymore when testing using Appium

I am creating an automation test using Appium and webdriverio:
const wdio = require("webdriverio");
const opts = {
path: "/wd/hub",
port: 4723,
capabilities: {
platformName: "Android",
platformVersion: "11",
deviceName: "Android Emulator",
app: "/path/to/myapk.apk",
automationName: "UiAutomator2",
autoGrantPermissions: true
}
};
async function main() {
const driver = await wdio.remote(opts);
const contexts = await driver.getContexts();
console.log("Contexts:", contexts);
await driver.deleteSession();
}
main();
The problem
When running tests I could see that I used to have two contexts:
NATIVE_APP
WEBVIEW_chrome (or similar, I do not remember exactly the value here)
I then made a change which switched contexts to the webview, there I got an error about the chrome driver not being found. That is when I installed it: npm install "appium-chromedriver".
I do not know if this is what made everything go babanas, but since then, everytime I test, I can only see the native context, no more webview context :(
More info
It is important to point out that I have modified my Android app to include this:
#Override
public void onCreate() {
super.onCreate();
WebView.setWebContentsDebuggingEnabled(true);
}
I can also start chrome://inspect and see the webview is there and even inspect it. But when running tests, the driver cannot see the webview context.
Why? How to fix this?
Turns out that I need to wait for a webview to show up in the app, so this works:
async function main() {
const driver = await wdio.remote(opts);
// Wait a few seconds so the webview properly loads
await new Promise(resolve => setTimeout(resolve, 5000));
const contexts = await driver.getContexts();
console.log("Contexts:", contexts);
await driver.deleteSession();
}

Resources