I have a react component that contains an SVG image.
and i import it like this
import MessageIcon from '../static/styles/svg/messageIcon';
However as part of code splitting, i want to dynamically import it instead of static import.
I tried like this but does not work
import('../static/styles/svg/messageIcon').then(MessageIcon => { return <MessageIcon /> });
can anyone tell me how i can dynamically import and call this component? thanks in advance
You can import dynamical like this :
import React, { useEffect, useState } from "react";
const App = () => {
const [state, setState] = useState<any>();
useEffect(() => {
(async () => {
const i = await import("../../assets/img/user.svg");
setState(i);
})();
}, []);
return <img alt={"test dynamic"} src={state?.default} />;
};
export default App;
Also, you can write it with ref too:
import React, { useEffect, useRef } from "react";
const App = () => {
const ref = useRef<any>();
useEffect(() => {
(async () => {
const i = await import("../../assets/img/user.svg");
ref.current.src = i.default;
})();
}, []);
return <img alt={"test dynamic"} ref={ref} />;
};
export default App;
import dynamical React component
import React from "react";
const App = () => {
const nameIcon = "iconMessage";
const IconMessage = React.lazy(() => import(/* webpackChunkName: "icon" */ `./${nameIcon}`));
return <IconMessage />;
};
export default App;
I am trying to implement a theme provider in Gatsby using the wrapRootElement browser API. I've been looking at examples online and can't find where I went wrong. I am getting an error "Objects are not valid as a React child (found: object with keys {children})."
This is the first time I'm using Gatsby browser API, I know the problem is with the children I'm trying to pass down, with the element being an object, but looking at all the examples I can find online they are implemented the same way.
gatsby-browser.js
import React from "react"
import ThemeWrapper from './src/components/theme/theme'
export function wrapRootElement({ element }) {
return <ThemeWrapper>{element}</ThemeWrapper>
}
theme.tsx
import * as React from "react"
import { ThemeProvider } from "#material-ui/styles"
import { CssBaseline } from "#material-ui/core"
import ThemeContext from "./themecontext"
import { defaultTheme, darkTheme } from "./themedefinition"
const ThemeWrapper = (children: React.ReactNode) => {
const [isDarkTheme, setDarkTheme] = React.useState(false);
const toggleDark = () => {
setDarkTheme(!isDarkTheme);
}
React.useEffect(() => {
if (window.matchMedia("(prefers-color-scheme: dark)").matches === true) {
setDarkTheme(true);
}
}, [])
return (
<ThemeContext.Provider value={{isDarkTheme, toggleDark}}>
<ThemeProvider theme={isDarkTheme ? darkTheme : defaultTheme}>
<CssBaseline />
{children}
</ThemeProvider>
</ThemeContext.Provider>
)
}
export default ThemeWrapper
Looks like a simple typo: you aren't destructuring children from your props, you're naming the first argument (the props) children.
- const ThemeWrapper = (children: React.ReactNode) => {
+ const ThemeWrapper = ({ children: React.ReactNode }) => {
Hi I am using gatsby and the build seems to be failing in this useContext file:
Gatsby build
Building static HTML failed for path "/components/DarkThemeContext/DarkThemeContext/"
DarkThemeContext.js
import React, { useState, useContext } from "react"
const DarkThemeContext = React.createContext({
darkMode: false,
setDarkMode: () => {},
})
export const DarkThemeProvider = ({ children }) => {
const [isDark, setIsDark] = useState(false)
return (
<DarkThemeContext.Provider
value={{
darkMode: isDark,
setDarkMode: setIsDark,
}}
>
{children}
</DarkThemeContext.Provider>
)
}
const useDarkThemeContext = () => useContext(DarkThemeContext);
export default useDarkThemeContext;
It fails in this file during build but the exact error it spits out is:
Objects are not valid as a React child (found: object with keys {darkMode, setDarkMode}). If you meant to render a collection of children, use an array instead.
I am not sure how and why this is it picking this up as children
Edit: included code for DarkThemeProvider render
gatsby-browser.js
import React from "react"
import { DarkThemeProvider } from './src/pages/components/DarkThemeContext/DarkThemeContext'
export const wrapRootElement = ({ element }) => {
return <DarkThemeProvider>{element}</DarkThemeProvider>
}
Edit: include code where context is accessed for darkMode
navDrawer.jsx
const { darkMode } = useDarkThemeContext()
const darkModeStyling = createMuiTheme({
palette: {
type: darkMode ? "dark" : "light",
},
})
...
<ThemeProvider theme={darkModeStyling}>
...
</ThemeProvider>
I was trying to react query for the first time then I got this at the start of my React app.
import React from 'react'
import { useQuery } from "react-query";
const fetchPanets = async () => {
const result = await fetch('https://swapi.dev/api/people')
return result.json()
}
const Planets = () => {
const { data, status } = useQuery('Planets', fetchPanets)
console.log("data", data, "status", status)
return (
<div>
<h2>Planets</h2>
</div>
)
}
export default Planets
As the error suggests, you need to wrap your application in a QueryClientProvider. This is on the first page of the docs:
import { QueryClient, QueryClientProvider, useQuery } from 'react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
While this is most commonly caused by not having your application wrapped in a <QueryClientProvider>, in my case it happened because I was importing some shared components, which ended up with a different context. You can fix this by setting the contextSharing option to true
That would look like:
import { QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient()
function App() {
return <QueryClientProvider client={queryClient} contextSharing={true}>...</QueryClientProvider>
}
From the docs: (https://react-query.tanstack.com/reference/QueryClientProvider)
contextSharing: boolean (defaults to false)
Set this to true to enable context sharing, which will share the first and at least one instance of the context across the window to ensure that if React Query is used across different bundles or microfrontends they will all use the same instance of context, regardless of module scoping.
Just make changes like below it will work fine
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
ReactDOM.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
document.getElementById('root')
);
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
const queryClient = new QueryClient();
const fetchPanets = async () => {
const result = await fetch('https://swapi.dev/api/people')
return result.json()
}
const Planets = () => {
const { data, status } = useQuery('Planets', fetchPanets)
console.log("data", data, "status", status)
return (
<div>
<h2>Planets</h2>
</div>
);
}
export default function Wraped(){
return(<QueryClientProvider client={queryClient}>
<Planets/>
</QueryClientProvider>
);
}
Single SPA (micro-frontend) - React Query v3.34.15
I was getting this error while trying to integrate a sigle-spa react parcel into the root application.
I used craco-plugin-single-spa-application for the building of a CRA app as a way to adapt it for a parcel. In the entry config I was pointing to my single-spa-react config.
// craco.config.js
const singleSpaApplicationPlugin = require('craco-plugin-single-spa-application')
module.exports = {
plugins: [
{
plugin: singleSpaApplicationPlugin,
options: {
orgName: 'uh-platform',
projectName: 'hosting',
entry: 'src/config/single-spa-index.cf.js',
orgPackagesAsExternal: false,
reactPackagesAsExternal: true,
externals: [],
minimize: false
}
}
]
}
In the single-spa-index.cf.js file I had the following configs.
import React from 'react'
import ReactDOM from 'react-dom'
import singleSpaReact from 'single-spa-react'
import App from '../App'
const lifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: App,
errorBoundary() {
return <div>Ocorreu um erro desconhecido!</div>
}
})
export const { bootstrap, mount, unmount } = lifecycles
After reading a bunch of forums and the react-query documentation, the only thing that I figured out I needed to change was pass in the QueryClientProvider the prop contextSharing as true. After had did this change, ran the building and access the route that opens my parcel. I got the same error.
import React from 'react'
import ReactDOM from 'react-dom'
import { QueryClient, QueryClientProvider } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'
import App from './App'
const queryClient = new QueryClient()
const isDevelopmentEnv = process.env.NODE_ENV === 'development'
if (isDevelopmentEnv) {
import('./config/msw/worker').then(({ worker }) => worker.start())
}
ReactDOM.render(
<React.StrictMode>
<QueryClientProvider contextSharing={true} client={queryClient}>
<App />
{isDevelopmentEnv && <ReactQueryDevtools initialIsOpen={false} />}
</QueryClientProvider>
</React.StrictMode>,
document.getElementById('root')
)
But, how do I solved that. Well, it was was simple. I couldn't even imagine why it was working locally. But not after building and integration.
The problem was because I put the React Query Provider inside the index o the application and in my single-spa-index.cf.js I was importing import App from '../App' which really wasn't wrapped by the provider. Once I also was importing App in the application index, where It was wrapped making It works locally. 😢😢
So after figure that out, my code was like that:
CODE AFTER SOLUTION
// craco.config.js
const singleSpaApplicationPlugin = require('craco-plugin-single-spa-application')
module.exports = {
plugins: [
{
plugin: singleSpaApplicationPlugin,
options: {
orgName: 'uh-platform',
projectName: 'hosting',
entry: 'src/config/single-spa-index.cf.js',
orgPackagesAsExternal: false,
reactPackagesAsExternal: true,
externals: [],
minimize: false
}
}
]
}
// src/config/single-spa-index.cf.js
import React from 'react'
import ReactDOM from 'react-dom'
import singleSpaReact from 'single-spa-react'
import App from '../App'
const lifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: App,
errorBoundary() {
return <div>Ocorreu um erro desconhecido!</div>
}
})
export const { bootstrap, mount, unmount } = lifecycles
// App.tsx
import { QueryClient, QueryClientProvider } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'
import { config } from 'config/react-query'
import Routes from 'routes'
import GlobalStyles from 'styles/global'
import * as S from './styles/shared'
const queryClient = new QueryClient(config)
const isDevelopmentEnv = process.env.NODE_ENV === 'development'
if (isDevelopmentEnv) {
import('./config/msw/worker').then(({ worker }) => worker.start())
}
function App() {
return (
<QueryClientProvider contextSharing={true} client={queryClient}>
<S.PanelWrapper>
<Routes />
<GlobalStyles />
</S.PanelWrapper>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
export default App
// index.tsx
import { StrictMode } from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
document.getElementById('root')
)
Well, it was long but I hope it helps someone that's undergoing for the same problem as mine. 🙌🙌🙌
I was trying to fix the same thing:
I followed the React Query docs
and used the concept of Higher Order Component
See if it helps:
import React from 'react';
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
import Planet from './Planet';
const queryClient = new QueryClient();
const fetchPlanets = async () => {
const res = await fetch('http://swapi.dev/api/planets/');
return res.json();
}
const Planets = () => {
const { data, status } = useQuery('planets', fetchPlanets);
return (
<div>
<h2>Planets</h2>
{ status === 'loading' && (<div>Loading data...</div>)}
{ status === 'error' && (<div>Error fetching data</div>)}
{
status === 'success' && (
data.results.map(planet =>
<Planet
key={planet.name}
planet={planet}
/>
)
)
}
</div>
)
}
// Higher order function
const hof = (WrappedComponent) => {
// Its job is to return a react component warpping the baby component
return (props) => (
<QueryClientProvider client={queryClient}>
<WrappedComponent {...props} />
</QueryClientProvider>
);
};
export default hof(Planets);
In my case I was importtng from 'react-query' in one place and '#tanstack/react-query' in another.
I got that error when trying to add the react-query devtools.
The problem was I was installing it wrongly according my version, I was using react-query v3.
WRONG FOR react-query V3 (GOOD FOR V4)
import { ReactQueryDevtools } from '#tanstack/react-query-devtools';
OK FOR react-query V3
import { ReactQueryDevtools } from 'react-query/devtools';
In my case I accidentally used two different versions of react-query in my modules.
In my case
Error import { QueryClient, QueryClientProvider } from "#tanstack/react-query";
Solution import { QueryClient, QueryClientProvider } from "react-query";
remove it #tanstack/
Just be careful when upgrade from react-query v3 to #tanstack/react-query v4.
Ensure that you replace all imports as "react-query" to "#tanstack/react-query" and then run yarn remove the lib that you won't use anymore, otherwise you may accidentally import the unexpected one.
This happened to me and caused this error.
I have a simple React application with the following App.js, App.test.js, and utils.js files:
App.js
import React from 'react';
import { randomNameGenerator } from './utils.js';
import './App.css';
function App() {
return (
<div>
{randomNameGenerator()}
</div>
);
}
export default App;
App.test.js
import React from 'react';
import { render } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect'
import App from './App';
it('allows Jest method mocking', () => {
const { getByText } = render(<App />);
expect(getByText("Craig")).toBeInTheDocument()
});
utils.js
export function randomNameGenerator() {
return Math.floor((Math.random() * 2) + 1) == 1 ? 'Steve' : 'Bill';
}
This is a simple example, but what I'm trying to accomplish is a Jest mock of the randomNameGenerator() function to only return "Craig" for that specific Jest test.
I've followed a wide variety of tutorials/guides, but can't find anything that works - the closest (by "feel") that I've gotten was this (in App.test.js), which had no effect:
jest.doMock('./utils', () => {
const originalUtils = jest.requireActual('./utils');
return {
__esModule: true,
...originalUtils,
randomNameGenerator: jest.fn(() => {
console.log('## Returning mocked typing duration!');
return 'Craig';
}),
};
})
The way it fails is expected:
Unable to find an element with the text: Craig. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
<body>
<div>
<div>
Steve
</div>
</div>
</body>
6 | it('allows Jest method mocking', () => {
7 | const { getByText } = render(<App />);
> 8 | expect(getByText("Craig")).toBeInTheDocument()
| ^
9 | });
You can mock the module by calling jest.mock, and then import it, then inside your tests you call mockImplementation to setup the right return value.
import React from 'react';
import { render } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect'
import App from './App';
import { randomNameGenerator } from "./utils";
jest.mock('./utils.js', () => ({
randomNameGenerator: jest.fn()
}));
describe('test', () => {
it('allows Jest method mocking 1', () => {
randomNameGenerator.mockImplementation(() => "Craig");
const { getByText } = render(<App />);
expect(getByText("Craig")).toBeInTheDocument()
});
it('allows Jest method mocking 2', () => {
randomNameGenerator.mockImplementation(() => "Not Craig");
const { getByText } = render(<App />);
expect(getByText("Not Craig")).toBeInTheDocument()
});
});