I'm using the example with-redux-persist repo paired with next.js found here: https://github.com/vercel/next.js/tree/canary/examples/with-redux-persist
When using a custom loading component, adding a query parameter to the URL, ie ?test=123, causes the page to hang on loading indefinitely. There are no errors reported in console and the redux state info looks clean.
Here is my _app.js I'm using to test this:
import { useStore } from '../store'
import { Provider } from 'react-redux'
import { persistStore } from 'redux-persist'
import { PersistGate } from 'redux-persist/integration/react'
export default function App({ Component, pageProps }) {
const store = useStore(pageProps.initialReduxState)
const persistor = persistStore(store)
return (
<Provider store={store}>
<PersistGate loading={<div>Loading</div>} persistor={persistor}>
<Component {...pageProps} />
</PersistGate>
</Provider>
)
}
You can reproduce it by cloning the above repo, modifying _app.js to look like above, starting the server with yarn dev and then finally visiting http://localhost:3000/?test=123.
Any help or insight into why this is occurring is appreciated!
Related
To create a site, I use nextjs, when creating pages, I took the general layout with the header and footer into a separate hoc component and wrapped the page components in the file with it _app.jsx:
function App({ Component, ...rest }) {
const { store, props } = wrapper.useWrappedStore(rest)
return (
<Provider store={store}>
<Layout>
<Component {...props.pageProps} />
</Layout>
</Provider>
)
}
Everything worked fine until localization became a problem, after using the next-18next library for translations and adding serverSideTranslations, two errors began to appear on each page:
react-i18next:: You will need to pass in an i18next instance by using initReactI18next
frontend-node_1 | TypeError: Cannot read properties of undefined (reading 'label')
frontend-node_1 | at DropdownSwitcher (webpack-internal:///./src/components/header/translation/DropdownSwitcher.jsx:45:36)
frontend-node_1 | at renderWithHooks (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5658:16)
frontend-node_1 | at renderIndeterminateComponent (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5731:15)
frontend-node_1 | at renderElement (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5946:7)
frontend-node_1 | at renderMemo (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5868:3)
frontend-node_1 | at renderElement (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:6011:11)
frontend-node_1 | at renderNodeDestructiveImpl (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:6104:11)
frontend-node_1 | at renderNodeDestructive (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:6076:14)
frontend-node_1 | at renderNode (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:6259:12)
frontend-node_1 | at renderHostElement (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5642:3)
The error with "label" occurs because the i18n object is empty on the server:
const DropdownSwitcher = () => {
const { i18n } = useTranslation()
const currentLanguage = useMemo(() => { // language as undefined
return LANGUAGES.find((item) => item.language === i18n.language)
}, [i18n.language])
....
But everything is fine on the client and there are no errors. What could be the reason and how to fix it, since the App itself from the _app.jsx file is wrapped in appWithTranslation from next-i18next.
Therefore, two questions arise, how to fix react-i18next:: You will need to pass in an i18next instance by using initReactI18next and why there is no i18n object on the server?
I moved the layout to the level of the page itself, removing it from _app.js, but for some reason, then something, useEffect() is repeated in the header, although the header component has not changed in any way and bringing the layout to the level of _app.jsx fixes it
If there is not enough information or you need a visual example, I will try to create a small program that demonstrates this with open source. Please write in a comment.
I solved my problem, but I forgot to provide an answer here, but I noticed that someone also has this problem, so I will try to help people who come across this post, although it is relevant only for nextjs version 12, since with the appearance of version 14, the structure there has improved a lot with as I think there should be no more questions like mine.
1. Rendering the layout
In the official doc, there is a whole section that describes how to correctly divide the layout so that it works according to the SPA type.
pages/index.jsx
// pages/index.jsx
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
export default function Page() {
return (
/** Your content */
)
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
pages/_app.js
// pages/_app.js
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}
This component method approach is much better than using its direction in _app.jsx because you can extend or replace them and not make a crude monolith, example how I used it:
// pages/ingex.jsx
function HomePage() {
return (
<HomeLayout>
<Main />
</HomeLayout>
)
}
HomePage.getLayout = (page) => <MainLayout>{page}</MainLayout>
// pages/about-us.jsx
const AboutUsPage = () => {
return (
<>
<HomeLayout>
<AboutUs />
</HomeLayout>
</>
)
}
AboutUsPage.getLayout = (page) => (
<MainLayout withNav>
<LayoutContext.Consumer>
{({ device }) => device.isMobile && <NavigationMobile />}
</LayoutContext.Consumer>
{page}
</MainLayout>
)
With this approach, react still works like a spa and a similar page to about-us, which will also have NavigationMobile, will simply compare it.
2. Error with next-i18next
The whole point was that the next-i18next library was configured incorrectly in the first place (more precisely, it needed to be corrected). In order to configure everything correctly, I had to do the following:
- Move the folder with translation files to the public folder. This is necessary so that the library config, which we will configure a little below, can see the translation files and interact with them
- Configure next-i18next.config.js to work with the client. Here is an example setup with some comments. And also a link to the documentation, and some other resources I found while setting up.
next-i18next.config.js
const path = require('path')
const LANGUAGES = ['en', 'pl', 'uk']
const DEFAULT_LANGUAGE = 'en'
// if it is the server, then the full path, if the client, then the relative path.
const localePath =
typeof window === 'undefined' ? path.resolve('public', 'translation') : '/public/translation'
module.exports = {
i18n: {
defaultLocale: DEFAULT_LANGUAGE,
locales: LANGUAGES,
fallbackLng: LANGUAGES,
nsSeparator: '::',
keySeparator: '::',
// How to use libraries for i18next like LanguageDetector
use: [require('i18next-intervalplural-postprocessor')],
serializeConfig: false,
},
localePath: localePath,
}
- Configure next-i18next in the _app.jsx file. Here everything is as described in the documentation.
import { appWithTranslation } from 'next-i18next'
import nextI18NextConfig from '../../next-i18next.config'
function App({ Component, ...rest }) {
const { store, props } = wrapper.useWrappedStore(rest)
const getLayout = Component.getLayout || ((page) => page)
//WARNING!!! You don't have to have your own i18next initialization like i18next.use(LanguageDetector).use(intervalPlural).init({ detection: options }) this is all done by the next-i18next library
return (
<Provider store={store}>
<AppHOC>{getLayout(<Component {...props.pageProps} />)}</AppHOC>
</Provider>
)
}
export default appWithTranslation(App, nextI18NextConfig)
- You need to pass the config when calling the serverSideTranslations function. To make your life easier, it is better to transfer the implementation of this function to another file, here is an example of how I did it:
// utils/serverSideTranslations.js
import { serverSideTranslations as baseServerSideTranslations } from 'next-i18next/serverSideTranslations'
import { dt } from '../../constants/defaultTranslate'
import { DEFAULT_LANGUAGE } from '../../constants/languages'
import nextI18NextConfig from '../../../next-i18next.config.js'
const serverSideTranslations = async (locale, domains = []) => {
return await baseServerSideTranslations(locale, [...dt, ...domains], nextI18NextConfig, [
DEFAULT_LANGUAGE,
])
}
export default serverSideTranslations
- And finally, use this function on the pages.
import MainLayout from '../components/layouts/MainLayout'
import serverSideTranslations from '../utils/serverSideTranslations'
import HomeLayout from '../components/home/HomeLayout'
import Main from '../components/home/main/Main'
function HomePage() {
return (
<HomeLayout>
<Main />
</HomeLayout>
)
}
HomePage.getLayout = (page) => <MainLayout>{page}</MainLayout>
export const getServerSideProps = async ({ locale }) => {
// Wrapping in Promis.all is not necessary, I use it simply so that if there are any other asynchronous operations, then not to use them through await and not to block each other's work
const [translations] = await Promise.all([
serverSideTranslations(locale, ['home']),
])
return {
props: {
...translations,
},
}
}
export default HomePage
I hope this helped someone, if you have any comments, write in the comments
Can someone help me understand if there is a way to configure open Telemetry on the client side for react and vanilla JS projects all I want to do is to console the traces of fetch call that are being made from the browser.
Most of the documentation I see is only for nodejs. Pls pinpoint a documentation if there are any?
The documentation gives a common guide for Javascript. What you do for you React would be same as what you do for Node.js or even simple JS scripts.
Just follow the documentation. Create and export a tracer:
import { ZoneContextManager } from '#opentelemetry/context-zone';
import { registerInstrumentations } from '#opentelemetry/instrumentation';
import { DocumentLoadInstrumentation } from '#opentelemetry/instrumentation-document-load';
import { FetchInstrumentation } from '#opentelemetry/instrumentation-fetch';
import { UserInteractionInstrumentation } from '#opentelemetry/instrumentation-user-interaction';
import { XMLHttpRequestInstrumentation } from '#opentelemetry/instrumentation-xml-http-request';
import { ConsoleSpanExporter, SimpleSpanProcessor } from '#opentelemetry/sdk-trace-base';
import { WebTracerProvider } from '#opentelemetry/sdk-trace-web';
const setupTracer = () => {
const provider = new WebTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
provider.register({
// Changing default contextManager to use ZoneContextManager - supports asynchronous operations - optional
contextManager: new ZoneContextManager(),
});
// Registering instrumentations
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation(),
new UserInteractionInstrumentation(),
new XMLHttpRequestInstrumentation(),
new FetchInstrumentation()
],
});
}
export default setupTracer;
Import the tracer like this in your app's entry point (usually index.js):
setupTracer();
ReactDOM.render(<App />, document.getElementById('root'));
I am facing an error while trying to complete the official shopify tutorial about developing shopify app.
I have been following the tutorial step by step but even then ran in to problem where the error is thrown that my config file is invalid as it does not contain the host.
My _app.js file code is as follow:
import React from "react";
import App from "next/app";
import Head from "next/head";
import { AppProvider, Frame } from "#shopify/polaris";
import "#shopify/polaris/dist/styles.css";
import translations from "#shopify/polaris/locales/en.json";
import { Provider } from "#shopify/app-bridge-react";
import ClientRouter from "../components/ClientRouter";
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin } = this.props;
const config = {
apiKey: API_KEY,
shopOrigin,
forceRedirect: true,
};
console.log(config);
return (
<React.Fragment>
<Head>
<title>Sample App</title>
<meta charSet="utf-8" />
</Head>
<Provider config={config}>
<ClientRouter />
<AppProvider i18n={translations}>
<Frame>
<Component {...pageProps} />
</Frame>
</AppProvider>
</Provider>
</React.Fragment>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
shopOrigin: ctx.query.shop,
};
};
export default MyApp;
the console for the config file gives the right shopOrigin.
Ill be thankful if anyone helps
At the moment use the App Bridge version 2 is required like App review team points out:
Ensure that your app is on Shopify App Bridge version 2.0 to embed
your app within merchants' admin...
So, what I did was persist the shop name (Ex: toys.myshopify.com) in my database, encode to base64 and send like "host" query param when is required.
const host = Buffer.from(`${shop}/admin`).toString('base64');
ctx.redirect(`${process.env.HOST}?shop=${shop}&host=${host}`);
Note how i added "/admin" after the shop values, this is an important step, otherwise you get a 404 error (Page not found).
I'm trying to use the react material-ui theme having installed it from npm, I get the following errors when I include 'import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";' in boot-client.tsx:
TS7016: Could not find a declaration file for module
'material-ui/styles/MuiThemeProvider'.
'W:/web/WebFront/node_modules/material-ui/styles/MuiThemeProvider.js'
implicitly has an 'any' type. Try npm install
#types/material-ui/styles/MuiThemeProvider if it exists or add a new
declaration (.d.ts) file containing declare module
'material-ui/styles/MuiThemeProvider';
I've tried both suggestions to no avail including running the command: npm install -D #types/material-ui.
My #types folder in node_modules exists with the relevant types.
Here is the code where I'm trying to use it:
import './css/site.css';
import 'bootstrap';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createBrowserHistory } from 'history';
import configureStore from './configureStore';
import { ApplicationState } from './store';
import * as RoutesModule from './routes';
let routes = RoutesModule.routes;
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
// Create browser history to use in the Redux store
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
const history = createBrowserHistory({ basename: baseUrl });
// Get the application-wide store instance, prepopulating with state from the server where available.
const initialState = (window as any).initialReduxState as ApplicationState;
const store = configureStore(history, initialState);
function renderApp() {
// This code starts up the React app when it runs in a browser. It sets up the routing configuration
// and injects the app into a DOM element.
ReactDOM.render(
,
document.getElementById('react-app')
);
}
renderApp();
// Allow Hot Module Replacement
if (module.hot) {
module.hot.accept('./routes', () => {
routes = require<typeof RoutesModule>('./routes').routes;
renderApp();
});
}
Ok I figured it out, in tsconfig.json under 'compilerOptions' visual-studio by default had its types set to ["webpack-env"], I needed to add "material-ui" to it or optionally just remove it: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
Use the default import from the same path.
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
The solution that worked for me which is med's answer above which I explain in more detail below.
Open the tsconfig.json file. Add "types":"material-ui", within "compilerOptions": {}
as in
"compilerOptions": {"types":"material-ui"}
I want to import a React component from a jsx file in a template and render it in the template with ReactDOM. Later in production I would only want to ship react and all the dependencies of the component only when a site is loaded that has that component.
I have created a React component like this:
editor.jsx
import * as React from "react";
import {Editor} from "draft-js-plugins-editor";
const plugins = [];
export class EditorComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty(),
};
}
onChange(editorState) {
this.setState({
editorState,
});
}
render() {
return (<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
/>);
}
}
http://www.phoenixframework.org/docs/static-assets suggests the require syntax for accessing module exports. So I added the following to my template <script>const editor = require("web/static/js/editor").EditorComponent</script>. This does not work though, because the browser cannot interpret require (or brunch does not pick it up).
I configured brunch like so:
plugins: {
babel: {
// Do not use ES6 compiler in vendor code
ignore: [/web\/static\/vendor/],
presets: ["es2015","react"]
}
},
modules: {
autoRequire: {
"js/app.js": ["web/static/js/app"],
"js/editor.jsx": ["web/static/js/editor"]
}
},
I am a bit lost here. How can this be done?
One idea that pops to mind is to create a JS file and import it in the template you want with a <script> tag. In the same template create an empty <div id=editor>. Then, in the JS file import React and ReactDOM and the component you want and use something like this:
ReactDOM.render(
<Editor/>,
document.getElementById("editor")
)
However, I'm not sure I understand your problem correctly.