I have an app.js with the following const function:
const serverUrl = process.env.REACT_APP_SERVER_URL;
const { getAccessTokenSilently } = useAuth0();
const callEndpoint = async (props) => {
const {endpoint} = props;
const token = await getAccessTokenSilently();
try {
const token = await getAccessTokenSilently();
console.log('endpoint is:', `${serverUrl}/${props.endpoint}`);
const response = await fetch(
`${serverUrl}/${props.endpoint}`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
const responseData = await response.json();
console.log(responseData.message);
} catch (error) {
console.log(error.message);
}
};
Further down in the render:
<Button variant="outline-primary" onClick = { (e) => {callEndpoint({endpoint:"get_timestamp_public"})}} >Fetch Public Data</Button>
It works fine.
However, when I try to move my endpoint into a new js file called 'api_funcs.js':
import React, { useEffect, useState } from 'react';
import { useAuth0 } from '#auth0/auth0-react';
const UseAPI = (props) => {
const {endpoint} = props;
const serverUrl = process.env.REACT_APP_SERVER_URL;
console.log("I am in here");
const { getAccessTokenSilently } = useAuth0();
const callEndpoint = async () => {
const token = await getAccessTokenSilently();
try {
// const token = await getAccessTokenSilently();
const response = await fetch(
`${serverUrl}/{props.endpoint}`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
const responseData = await response.json();
//setMessage(responseData.message);
console.log(responseData.message);
} catch (error) {
//setMessage(error.message);
console.log(error.message);
}
};
}
export default UseAPI;
The const { getAccessTokenSilently } = useAuth0(); throws me an error of:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
I don't understand -- isn't my UseAPI a function component? What am I missing?
As you are not breaking the Rules of Hooks.
There might be two common reasons because of which you might be seeing this error:
You might have mismatching versions of React and React DOM.
You might be using a version of react-dom (< 16.8.0) that doesn’t yet support Hooks. You can run
> npm ls react-dom
in your application folder to check which version you’re using.
You might have more than one copy of React in the same app.
In order for Hooks to work, the react import from your application code needs to resolve to the same module as the react import from inside the react-dom package.
If these react imports resolve to two different exports objects, you will see this warning. This may happen if you accidentally end up with two copies of the react package.
If you use Node for package management, you can run this check-in your project folder:
npm ls react
If you see more than one React, you’ll need to figure out why this happens and fix your dependency tree. For example, maybe a library you’re using incorrectly specifies react as a dependency (rather than a peer dependency). Until that library is fixed, Yarn resolutions is one possible workaround.
You can also try to debug this problem by adding some logs and restarting your development server:
// Add this in node_modules/react-dom/index.js window.React1 =
require('react');
// Add this in your component file require('react-dom'); window.React2
= require('react'); console.log(window.React1 === window.React2);
If it prints false then you might have two Reacts and need to figure out why that happened. This issue includes some common reasons encountered by the community.
This problem can also come up when you use npm link or an equivalent. In that case, your bundler might “see” two Reacts — one in application folder and one in your library folder. Assuming myapp and mylib are sibling folders, one possible fix is to run npm link ../myapp/node_modules/react from mylib. This should make the library use the application’s React copy.
Thus, running the command
npm link <path_to_local_library>/node_modules/react
might solve your issue.
Note
In general, React supports using multiple independent copies on one page (for example, if an app and a third-party widget both use it). It only breaks if require('react') resolves differently between the component and the react-dom copy it was rendered with.
Source React Doc
Related
I am creating a netflix like app for portfolio purposes and I am facing this error: Unhandled Rejection(TypeError): Failed to fetch.
in this part of my code there seems to be the problem:
const [movies, setMovies] = useState([]);
useEffect(() => {
fetch("https://movies-couch-api-herra17.vercel.app/movies")
.then((response) => response.json())
.then((data) => {
console.log(data);
precisely at .then((response...
Could someone share a clue or maybe would know what else I would need to review.
I have an API that is working. The client component is structured with React.
I have the following dependencies: prop-types, react, react-dom, parcel/transformer-sass.
Additionally this is how my index.jsx looks like:
import { createRoot} from 'react-dom/client';
import { MainView } from './components/main-view/main-view';
// Import statement to indicating bundle is needed import "./index.scss";
// Main component (will eventually use all others)
const MoviescouchApplication = () => { return <MainView />; }; // Find the root of your app const container = document.querySelector("#root"); const root = createRoot(container);
// Tells React to render your app in the root DOM element
root.render(<MoviescouchApplication/>);
It's due to the API at https://movies-couch-api-herra17.vercel.app/movies preventing cross-domain origins from accessing it. If you try fetching that API in your console, you should be able to see the same error.
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
}
)
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
}
)
This is probably more of a JavaScript/TypeScript question then it is about React/Testing.
But I'll give the complete story. So I have a test app with basic routing functionality and tests to verify that the routing works.
App.tsx
https://github.com/Leejjon/pwa-seo/blob/6f621968de1184b03744a262a68d291b4571c5c1/src/App.tsx
App.test.tsx
https://github.com/Leejjon/pwa-seo/blob/6f621968de1184b03744a262a68d291b4571c5c1/src/App.test.tsx
Everything worked fine. Then I added an useEffect hook to initialize my internationalization library:
useEffect(() => {
async function initMessages() {
await intl.init({
currentLocale: "en-US",
locales
});
}
initMessages().then(() => setLoading(false));
}, [loading]);
This loads all my text assets in English. This works fine, but broke all my tests with the following error message:
Warning: An update to App inside a test was not wrapped in act(...).
After some reading up on the internet I managed to fix my tests by adding this 'act' function, here is one example:
import React from 'react';
import {act, render, fireEvent, waitForElement} from '#testing-library/react';
import "#testing-library/jest-dom/extend-expect";
import App from './App';
test('Verify home page header', async() => {
let app: HTMLElement;
await act(async () => {
const {container} = render(<App/>);
app = container;
});
// #ts-ignore
if (app) {
const pageHeaderContent = app.querySelector("#pageHeader")?.firstChild?.textContent;
expect(pageHeaderContent).toMatch('Home page');
} else {
fail("The app should have been initialized.");
}
});
Right now I'm suppressing the TS2454: Variable 'app' is used before being assigned. warning with the #ts-ignore. This is ugly. If I move my assertions into the act function, I get the same Warning: An update to App inside a test was not wrapped in act(...). error again.
Is there a way to obtain the container object destructured from the render function without having to use the #ts-ignore and the if clause to do null checking?
I created a tag for the current code related to this question:
https://github.com/Leejjon/pwa-seo/releases/tag/uglylines
Link to last commit: https://github.com/Leejjon/pwa-seo/commit/2434f78c0619be2d55f9de965149f6bd6d1a0b90
Typescript is complaining about the app variable to not have been initialised when you access it in the if-statement. You can simply fix that by assigning null to it.
let app: HTMLElement = null;
In case you use strict null checks you have to allow null on the type:
let app: HTMLElement | null = null;
After puzzling this is my result
test('Verify home page header', async() => {
let app: HTMLElement | undefined = undefined;
await act(async () => {
const {container} = render(<App/>);
app = container;
});
let appAsHtmlElement = (app as unknown as HTMLElement);
const pageHeaderContent = appAsHtmlElement.querySelector("#pageHeader")?.firstChild?.textContent;
expect(pageHeaderContent).toMatch('Home page');
});
Better suggestions (if there is some way of not having to use the 'act' function) are still welcome.