I have a Nextjs app. I have used it to build the Coinbase clone. Since it uses window object to connect to metamsk, I need to disable the SSR in Nextjs. But some how if I disable the SSR and restart the server it's breaking my styles. Take a look below, check how the style of Navbar title Assets changes when I disable SSR and restart the dev server:
With SSR enabled:
With SSR disabled:
Here's my _app.js:
import dynamic from "next/dynamic";
import { ThemeProvider, createTheme } from '#mui/material/styles';
import CssBaseline from '#mui/material/CssBaseline';
import { ChainId, ThirdwebProvider } from "#thirdweb-dev/react";
import { MoralisProvider } from "react-moralis";
import NoSSR from "./NoSSR";
import '../styles/globals.css'
const darkTheme = createTheme({
palette: {
mode: 'dark',
background:{
dark:'#0a0b0d',
}
},
});
function MyApp({ Component, pageProps }) {
return (
<NoSSR>
<ThemeProvider theme={darkTheme}>
<CssBaseline/>
{/* <ThirdwebProvider desiredChainId={ChainId.Rinkeby}>
</ThirdwebProvider> */}
<MoralisProvider serverUrl={'https://124a8yab5jee.usemoralis.com:2053/server'} appId='Seyf64uxlgqgxt5Y75p1M4Hq21CC5osXcvj4T8Yw'>
<Component {...pageProps} />
</MoralisProvider>
</ThemeProvider>
</NoSSR>
)
}
export default MyApp;
NoSSR.js:
import dynamic from 'next/dynamic'
import React from 'react'
const NoSsr = props => (
<React.Fragment>{props.children}</React.Fragment>
)
export default dynamic(() => Promise.resolve(NoSsr), {
ssr: false
})
I am not getting why it's happening, is it a bug in Nextjs? I am also using Material UI libaray.
The fact that you used NoSSR in your _app.js and as a wrapper for the whole project means you don't want SSR option for any part of your code which is confusing because if you don't need SSR you don't have to go through nextjs. If you have a single component that needs to run on client side I suggest you only wrap that component on your NoSSR.
Also I suggest to put your globals.css in src folder (src is in root)
Related
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
For abstraction purpose, the app I work on has a custom provider that wraps a couple of other providers (like IntlProvider and CookiesProvider). The version of react-intl we use in our app is still v2 (react-intl#2.8.0). A simplified version of my App.js is:
App.js
return (
<Provider
value={{
localeData,
env,
data
}}>
<IntlProvider locale={language} key={language} messages={allMessages}>
{props.children}
</IntlProvider>
</Provider>
)
I have setup a custom render to test components in my app. My custom render looks exactly as it is specified in the react-intl-docs. I have followed the official setup guides from react-testing-library.
import React from "react";
import {render} from "#testing-library/react";
import {MyProvider} from "MyProvider";
const MyTestProvider = ({children}) => {
return <MyProvider>{children}</MyProvider>;
};
const myTestRender = (ui, options) => render(ui, {wrapper: MyTestProvider, ...options});
export * from "#testing-library/react";
export {myTestRender as render};
I then render my component under test as follows:
import {render as renderSC} from "test-utils";
import MyComponentUnderTest from 'MyComponentUnderTest';
test("does my component render", () => {
const {getByText} = renderSC(<MyComponentUnderTest />);
});
I get an error from react-intl which indicates it is warning, but the component that is rendered in test is empty.
console.error
Warning: IntlProvider.shouldComponentUpdate(): Returned undefined instead of a boolean value. Make sure to return true or false.
This is what my component renders in test:
<body>
<div />
</body>
Is anyone able to advise what I might be doing wrong?
I think your exports make this problem, try these instead:
import React from "react";
import {render} from "#testing-library/react";
import {MyProvider} from "MyProvider";
const MyTestProvider = ({children}) => {
return <MyProvider>{children}</MyProvider>;
};
const myTestRender = (ui, options) => render(ui, {wrapper: MyTestProvider, ...options});
export default myTestRender;
and then use your custom renderer this way:
import renderSC from "test-utils";
import MyComponentUnderTest from 'MyComponentUnderTest';
test("does my component render", () => {
const {getByText} = renderSC(<MyComponentUnderTest />);
});
So I was able to confirm that nothing was wrong with testing-library or with react-intl. The problem was the app I work on had a module mock that mocked out the functionality of react-intl. This was done for convenience when working with unit tests with Enzyme. However this module mock was stripping out all functionality I needed for IntlProvider and that was the reason I was seeing an empty div when I render the testing-library test with a wrapper.
I have a site built with React Static that has a Header component that is always present. Depending on if the current page has a hero component or not, the Header should be either light or dark.
The Header is rendered outside of the routes and the useEffect is triggered before the children is rendered. This is probably because of the routing.
This is the current code:
// App.js
import React, { useState, useEffect } from 'react'
import { Root, Routes } from 'react-static'
export default () => {
const [useDarkTheme, setUseDarkTheme] = useState(false);
useEffect(() => {
if (typeof document !== "undefined") {
const heroPresent = document.querySelectorAll(".o-hero").length > 0;
console.log("The hero is present: " + heroPresent);
setUseDarkTheme(!heroPresent);
}
})
return (
<Root>
<React.Suspense fallback={ <em>Loading...</em> }>
<Header useDarkTheme={ useDarkTheme } />
<Routes default />
</React.Suspense>
</Root>
);
}
What will be rendered at <Routes default /> is the static pages configured in React Static's static.config.js.
Below is an example of the Hero component:
// Hero.js
import React from "react";
export default () => {
console.log("This is the Hero rendering. If this exist, the Header should be dark.");
return (
<div className="o-hero">
<p>Hero!</p>
</div>
);
}
When I run the application and look at the logs this is what I get:
The hero is present: false
This is the Hero rendering. If this exist, the Header should be dark.
How could I somehow detect the presence of the Hero from the Header although the Hero is in a router and the Header is not? This feels like quite a common use case, but I could not find any info on the interwebs.
Thanks in advance!
So I ended up using useContext to provide all children with a getter and a setter for the Header's theme (dark or light). The solution is very much inspired from this answer. The solution looks like this:
// App.js
import React, { useState, useContext } from 'react'
import { Root, Routes } from 'react-static'
import { HeaderThemeContext } from "./context";
export default () => {
const { theme } = useContext(HeaderThemeContext);
const [headerTheme, setHeaderTheme] = useState(theme);
return (
<Root>
<React.Suspense fallback={ <em>Loading...</em> }>
<HeaderThemeContext.Provider value={ { theme: headerTheme, setTheme: setHeaderTheme } }>
<Header theme={ headerTheme } />
<Routes default />
</HeaderThemeContext.Provider>
</React.Suspense>
</Root>
);
}
// Hero.js
import React from "react";
import { headerThemes, setHeaderTheme } from "./context";
export default () => {
setHeaderTheme(headerThemes.DARK);
console.log("This is the Hero rendering. If this exist, the Header should be dark.");
return (
<div className="o-hero">
<p>Hero!</p>
</div>
);
}
// context.js
import React, { createContext, useContext } from "react";
export const headerThemes = {
LIGHT: "light",
DARK: "dark",
};
export const HeaderThemeContext = createContext({
theme: headerThemes.LIGHT,
setTheme: () => {}
});
// This is a hook and can only be used in a functional component with access to the HeaderThemeContext.
export const setHeaderTheme = theme => useContext(HeaderThemeContext).setTheme(theme);
This gives global access to set and get the header theme, which might not be optional, but it works for now and I think it's fine. Please let me know if there is a better way of doing this.
I don't know my issue is a bug or just a support term or just a configuration mismach, but I spend much time, Not happy go lucky writing here to somebody settle my issue. no!
I searched for 3 days, Even just sleep about 10 hours in this 3 days, try many ways, even I use Gitter but no one answer me, I see issues #10982 and #11506 and many Stack Overflow question and answers but I couldn't fix my issue and didn't find right way.
Absolutely I read and read many times the Material-UI Docs but I exactly did what the Docs said.
At last, I see class name hydration mismatch in server and client, 😞 this warning:
Warning: Prop `className` did not match. Server: "MuiFormLabel-root-134 MuiInputLabel-root-129 MuiInputLabel-formControl-130 MuiInputLabel-animated-133" Client: "MuiFormLabel-root-10 MuiInputLabel-root-5 MuiInputLabel-formControl-6 MuiInputLabel-animated-9"
I swear I tried many ways and searched a lot. I'm using Material-UI version 1.2.1.
This is my Index component as a root component:
import React, {Component} from 'react';
import Helmet from 'react-helmet';
import styles from 'StylesRoot/styles.pcss';
import TextField from '#material-ui/core/TextField';
export default class App extends Component {
render() {
return (
<div className={styles['root-wrapper']}>
<Helmet
htmlAttributes={{lang: 'fa', amp: undefined}}
bodyAttributes={{dir: 'rtl'}}
titleTemplate='اسکن - %s'
titleAttributes={{itemprop: 'name', lang: 'fa'}}
meta={[
{name: 'description', content: 'صفحه اتصال اعضاء'},
{name: 'viewport', content: 'width=device-width, initial-scale=1'},
]}
link={[{rel: 'stylesheet', href: '/dist/styles.css'}]}
/>
<TextField label='test' helperText='help'/>
</div>
);
};
}
Here below you can see my server.jsx and client.jsx:
//--------------------------------------------client.jsx
import React from 'react';
import {hydrate} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import {MuiThemeProvider, createMuiTheme} from '#material-ui/core/styles';
import {lightBlue, red} from '#material-ui/core/colors';
import Index from './app/index';
import RTL from './app/public/rtl';
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
class Main extends React.Component {
// Remove the server-side injected CSS.
componentDidMount() {
const jssStyles = document.getElementById('jss-server-side');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
render() {
return (
<BrowserRouter>
<Index {...this.props}/>
</BrowserRouter>
);
}
}
hydrate((
<RTL>
<MuiThemeProvider theme={theme}>
<Main/>
</MuiThemeProvider>
</RTL>
), document.getElementById('root'));
//--------------------------------------------server.jsx
import React from 'react';
import {renderToString} from 'react-dom/server';
import {SheetsRegistry} from 'react-jss/lib/jss';
import JssProvider from 'react-jss/lib/JssProvider';
import {StaticRouter} from 'react-router-dom';
import {Helmet} from "react-helmet";
import {MuiThemeProvider, createMuiTheme, createGenerateClassName} from '#material-ui/core/styles';
import {red, lightBlue} from '#material-ui/core/colors';
import Template from './app/template';
import Index from './app/index';
import RTL from './app/public/rtl';
export default function serverRenderer({clientStats, serverStats}) {
return (req, res, next) => {
const context = {};
const sheetsRegistry = new SheetsRegistry();
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
const generateClassName = createGenerateClassName();
const markup = renderToString(
<JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
<RTL>
<MuiThemeProvider theme={theme} sheetsManager={new Map()}>
<StaticRouter location={req.url} context={context}>
<Index/>
</StaticRouter>
</MuiThemeProvider>
</RTL>
</JssProvider>
);
const helmet = Helmet.renderStatic();
const jss = sheetsRegistry.toString();
res.status(200).send(Template({
markup: markup,
helmet: helmet,
jss: jss,
}));
};
}
So now it's needed to template.jsx:
export default ({ markup, helmet, jss }) => {
return `<!DOCTYPE html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
<style id='jss-server-side'>${jss}</style>
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id='root'>${markup}</div>
<script src='/dist/client.js' async></script>
</body>
</html>`;
};
Ok, Now it is possible that this question is asked What is RTL? and Why you wrap MuiThemeProvider inside it in both server and client?
So first see RTL component:
import React from 'react';
import {create} from 'jss';
import rtl from 'jss-rtl';
import JssProvider from 'react-jss/lib/JssProvider';
import {createGenerateClassName, jssPreset} from '#material-ui/core/styles';
const jss = create({plugins: [...jssPreset().plugins, rtl()]});
const generateClassName = createGenerateClassName();
export default props => (
<JssProvider jss={jss} generateClassName={generateClassName}>
{props.children}
</JssProvider>
);
I saw it in Material-UI Docs and I believe the documentation is a little poor and could be improved. I asked myself, Why documentation pass a props.children for this function? and I guess maybe this function should wrap something. so I try many shapes and it works. but in the first call in browser after build. when I refresh the browser then the warning appears 😞 and I see this damn shape:
Surely I wanna see below shape, but I see it just once after build:
I don't know what is going wrong. I leave an issue on Github too. And upload a mini repo for this issue, for seeing my issue just pull, and run npm install then num run dev. the project is accessible on localhost:4000
I don't know this way is proper or not, But it works well, Actually, I find out, using separate RTL Component, is not a good way, This cause to the inconsistency between server and client, I use the Right-to-Left Documentation page for each server and client-side separately, Hence omit the Rtl.jsx file and it's component from my project, So, server.jsx and client.jsx is like below:
//---------------------------------------------client.jsx
import React, {Component} from 'react';
import {hydrate} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import {
MuiThemeProvider, createMuiTheme,
createGenerateClassName, jssPreset
} from '#material-ui/core/styles';
import {create} from 'jss';
import rtl from 'jss-rtl';
import JssProvider from 'react-jss/lib/JssProvider';
import {lightBlue, red} from '#material-ui/core/colors';
import Index from './app/index';
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
const jssRTL = create({plugins: [...jssPreset().plugins, rtl()]});
const generateClassName = createGenerateClassName();
class Main extends Component {
componentDidMount() {
const jssStyles = document.getElementById('jss-server-side');
if (jssStyles) {
jssStyles.remove();
}
}
render() {
return (
<BrowserRouter>
<Index {...this.props}/>
</BrowserRouter>
);
}
}
hydrate((
<JssProvider jss={jssRTL} generateClassName={generateClassName}>
<MuiThemeProvider theme={theme}>
<Main/>
</MuiThemeProvider>
</JssProvider>
), document.getElementById('root'));
//---------------------------------------------server.jsx
import React from 'react';
import {renderToString} from 'react-dom/server';
import {SheetsRegistry} from 'react-jss/lib/jss';
import JssProvider from 'react-jss/lib/JssProvider';
import {StaticRouter} from 'react-router-dom';
import {Helmet} from "react-helmet";
import {
MuiThemeProvider, createMuiTheme,
createGenerateClassName, jssPreset
} from '#material-ui/core/styles';
import {create} from 'jss';
import rtl from 'jss-rtl';
import {red, lightBlue} from '#material-ui/core/colors';
import Template from './app/template';
import Index from './app/index';
export default function serverRenderer({clientStats, serverStats}) {
return (req, res, next) => {
const context = {};
const sheetsRegistry = new SheetsRegistry();
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
const jssRTL = create({plugins: [...jssPreset().plugins, rtl()]});
const generateClassName = createGenerateClassName();
const markup = renderToString(
<JssProvider jss={jssRTL}
registry={sheetsRegistry}
generateClassName={generateClassName}>
<MuiThemeProvider theme={theme} sheetsManager={new Map()}>
<StaticRouter location={req.url} context={context}>
<Index pathname={req.url}/>
</StaticRouter>
</MuiThemeProvider>
</JssProvider>
);
const helmet = Helmet.renderStatic();
const jss = sheetsRegistry.toString();
res.status(200).send(Template({
markup,
helmet,
jss,
}));
};
}
It works well on both sides, server, and client and makes consistent Material-UI CSS in style tag.
I'm teaching myself React-Native and I came across this strange roadblock. Inside my App.js I'm trying to export a class and then use another file within my App.js which is inside the somePage() function. where I'm calling <Header/> in an attempt for that text to appear on my physical device upon hitting refresh.
It displays <Login/> perfectly, but not what's within the somePage() function. My question is, why?
(I'm using Expo by the way, instead of having an index.ios.js file it's an App.js file that still supports cross platform development).
Here's my App.js file:
import React, { Component } from 'react';
import {AppRegistry} from 'react-native';
import Login from './components/Login';
import Header from './components/Header';
export default class Project extends Component {
render() {
return (
<Login/>
);
}
}
const somePage = () => (
<Header/>
);
AppRegistry.registerComponent('someProject', () => Project);
AppRegistry.registerComponent('someProject', () => somePage);
Here's my Header.js file:
import React from 'react';
import {Text} from 'react-native';
const Header = () => {
return <Text>Testing this out</Text>
}
export default Header;
The concept of react is that a parent component renders child components. You only need to register once because the root component is the parent component of all other components. Any other child or grandchild components you want to render must be descendants of the root component. As a side note, you don't need to have export default in front of the Project component, because you aren't exporting it anywhere: you are registering it below.
To fix your app, you need to place the header component inside the registered root component:
import React, { Component } from 'react';
import {AppRegistry, View } from 'react-native';
import Login from './components/Login';
import Header from './components/Header';
class Project extends Component {
render() {
return (
<View>
<Header/>
<Login/>
</View>
);
}
}
AppRegistry.registerComponent('someProject', () => Project);