I want to use a single Wrapper Component in a library im providing. The wrapper should provide stuff like Context and QueryClient. The component looks like this:
import { QueryClient, QueryClientProvider } from "react-query";
const client = new QueryClient();
function Wrapper(props) {
return (
<QueryClientProvider client={client}>
{props.children}
</QueryClientProvider>
);
}
When wrapping children with the Wrapper useQuery throws an error that no QueryClientProvider is set. In this case App uses the useQuery Hook from react-query.
import { Wrapper } from "my-lib";
ReactDOM.render(
<React.StrictMode>
<Wrapper>
<App />
</Wrapper>
</React.StrictMode>,
document.getElementById("root")
);
At first i thought you need a QueryClientProvider directly above an useQuery hook. But for example in Storybook you can define one QueryClient for all stories together. Where did i went wrong?
Edit:
App component:
import { useQuery } from "react-query";
export const App = () => {
const data = useQuery("data", () =>
fetch("https://jsonplaceholder.typicode.com/todos/1").then((res) =>
res.json()
)
);
if (data.isError || data.isLoading) return <>Error/Loading</>;
return <>{data.data.title}</>;
};
Well a few steps lead to my goal:
In the library, put react-query into the devDependencies and peerDependencies.
In the app, put react-query into the dependencies.
In the library, exclude react-query from being bundled by adding it to your bundler config. In my case it's the vite.config.ts:
const path = require("path");
const { defineConfig } = require("vite");
module.exports = defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: "my-lib",
formats: ["es", "umd"],
fileName: "my-lib",
},
rollupOptions: {
external: [
"react",
"react-dom",
"react-query",
],
},
},
});
The translations do not work I only see the keys. Instead of having "Welcome to MySite" I only have "welcome.title MySite".
my i18nextConf.js file
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
const fallbackLng = ['fr'];
const availableLanguages = ['en','fr'];
i18n
.use(Backend) // load translations using http (default public/assets/locals/en/translations)
.use(LanguageDetector) // detect user language
.use(initReactI18next) // pass the i18n instance to react-i18next.
.init({
fallbackLng, // fallback language is english.
backend: {
/* translation file path */
// loadPath: './translations/{{lng}}/{{ns}}.json'
loadPath: './translations/{{lng}}/common.json'
},
detection: {
checkWhitelist: true, // options for language detection
},
debug: false,
whitelist: availableLanguages,
interpolation: {
escapeValue: false, // no need for react. it escapes by default
},
});
export default i18n;
My index.js file
import React,{ Suspense } from 'react';
import './i18nextConf';
// code omitted
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<Suspense fallback={null}>
<App />
</Suspense>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
My translations files in src/translations/en/common.json and src/translations/fr/common.json
{
"welcome": {
"title": "Welcome to "
}
}
my CustomComponent.js
import React,{ Component } from 'react';
import { withTranslation } from "react-i18next";
class CustomComponent extends Component {
render() {
const { t } = this.props;
return (
<div className="section-title">
<h2 className={classes.myTitle}>{t('welcome.title')}</h2>
</div>
);
}
}
export default withTranslation()(CustomComponent);
Is there something wrong with my configuration ? What do I have to change ?
Those translation files will be served from the base path from where your index.html is also loaded and in-case it's an app created using create-react-app, that folder is public.
So I think when you are saying in loadPath that load files from ./translations/{{lng}}/common.json, the request actually gets resolved to public/translation/en/common.json but your files are located at src..... as you stated.
You can try either moving those files inside public (check this example for reference also ) or you can try the other syntax where you explicitly import the json from each file (inside src) and add it to a resources object which you pass to configuration as shown here
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 the following in App.js
let contracts = [
{
id: "1",
name: "Lloyds",
image: ""
}
];
class App extends Component {
render() {
return (
<div>
<Header />
<Search />
<ContractsList />
<Content />
<Footer />
</div>
);
}
}
export default App;
Im trying to pass the contracts variable as a prop in my index.js file
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import "bootstrap/dist/css/bootstrap-grid.css";
ReactDOM.render(
<React.StrictMode>
<App contracts={contracts} />
</React.StrictMode>,
document.getElementById("root")
);
but keep getting the following error:
Line 10:21: 'contracts' is not defined no-undef
how can i use the contracts variable so it can be used as a prop in other components?
You need to shift your contracts array from app.js to index.js and then pass it as a props.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import "bootstrap/dist/css/bootstrap-grid.css";
let contracts = [
{
id: "1",
name: "Lloyds",
image: ""
}
];
ReactDOM.render(
<React.StrictMode>
<App contracts={contracts} />
</React.StrictMode>,
document.getElementById("root")
);
contracts is not defined in your index.js file. Either you define contracts there, or you import it.
And then, you need to pass it to the underlying components in App.js too, for example:
<Content contracts={this.props.contracts} />
(For every component in which you need it.) Why are you trying to pass it in index.js at all?
The error is coming on the index.js file on this line:
<App contracts={contracts} />
here you dont have any variable called contracts, you may need to put a variable contracts in this file instead of App.js.
Hope this helps!
You've defined contracts in a different file to the one you're trying to use it in. Define contracts in index.js and it'll work.
you need to export the contracts
Demo
App.js
export const contracts = [
{
id: "1",
name: "Lloyds",
image: ""
}
];
index.js
import App,{contracts} from "./App";
For better. its already in app.js . so you can access with in app.js .no need to pass from index.js
I'm trying to setup a React/GraphQL App. This is my code. I double checked my imports/package.json but I keep getting a syntax error ("Unexpected token") from webpack on the 10th line and I just can't figure out why.
import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
const client = new ApolloClient({});
const Root = () => {
return(
<ApolloProvider client={client}>
<div>Lyrical</div>
</ApolloProvider>
);
};
ReactDOM.render(
<Root />,
document.querySelector('#root')
);
Make sure you have a .babelrc file in your main directory (parallel to webpack.config.js) that contains
{
"presets": ["env", "react"]
}
I'm not sure why ApolloProvider specifically needs this, but adding this simple line fixed it for me.