I've a problem to integrate the theming functionality inside my next.js project who use react-jss. I tried the ThemeProvider who I've found inside the documentation.
Everytime, my front-end page refresh two times. The first times, I can see that the CSS theme is apply but fews milliseconds later, the page refresh and the theme disappear.
Do you have an idea to fix my problem? Here are my files:
_document.jsx
import React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import {
SheetsRegistry,
JssProvider,
ThemeProvider,
} from 'react-jss';
const theme = {
colorPrimary: 'green',
};
export default class JssDocument extends Document {
static getInitialProps(ctx) {
const registry = new SheetsRegistry();
const page = ctx.renderPage(App => props => (
<ThemeProvider theme={theme}>
<JssProvider registry={registry}>
<App {...props} />
</JssProvider>
</ThemeProvider>
));
return {
...page,
registry,
};
}
render() {
return (
<html lang="en">
<Head>
<style id="server-side-styles">
{this.props.registry.toString()}
</style>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
index.jsx
import React from 'react';
import injectSheet from 'react-jss';
import PropTypes from 'prop-types';
const styles = theme => ({
myButton: {
backgroundColor: 'black',
color: theme.colorPrimary,
},
});
const Index = ({ classes }) => (
<div className={classes.myButton}>Welcome to Novatopo website!</div>
);
Index.propTypes = {
classes: PropTypes.shape({
myButton: PropTypes.string,
}).isRequired,
};
export default injectSheet(styles)(Index);
_app.jsx
import App from 'next/app';
export default class MyApp extends App {
componentDidMount() {
const style = document.getElementById('server-side-styles');
if (style) {
style.parentNode.removeChild(style);
}
}
}
Here a CodeSandbox to reproduce the problem: codesandbox.io/s/pyrznxkr1j
2 things you could do:
prepare a codesandbox example
use ThemeProvider inside of JssProvider
I resolved the problem. You need to integrate the ThemeProvider logic inside the _app.jsx file.
Like that:
export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
);
}
}
Related
I was trying to build a simple e-commerce website with Next.js + Redux + Tailwind.
The problem is in this the last 2 p tags are repeated:
I think the problem is with SSR in Next.js, index.js is rendered one time on server side and on client side too.
I don't know if getInitialProps would solve this issue.
I tried using class components in _app.js and adding getInitialProps into it, but it didn't work.
My /index.js is like:
import React from "react";
import { NavbarContainer } from "../components/Navbar/Navbar.container";
import {
Box,
} from "#chakra-ui/react";
const Home = () => {
return (
<>
<Box className="">
<NavbarContainer/>
<Box spacing={3} className="btmList"> iainicback</Box>
<Box spacing={3} className="btmList"> iainicback</Box>
</Box>
</>
);
};
export default Home;
My _app.js is like:
import App from "next/app";
import { Provider } from "react-redux";
import { store } from "../redux";
import React from "react";
import "../styles/globals.css";
import { extendTheme } from "#chakra-ui/react";
import { Chakra } from "../styles/Chakra";
// 2. Extend the theme to include custom colors, fonts, etc
const colors = {
brand: {
900: "#1a365d",
800: "#153e75",
700: "#2a69ac",
200: "#384d",
},
};
const theme = extendTheme({ colors });
const MyApp = (props) => {
const { Component, appProps } = props
return (
<Provider store={store}>
<Chakra theme={theme} cookies={appProps?.cookies}>
<Component {...appProps} />
</Chakra>
</Provider>
);
}
export default MyApp
This is my folder structure:
I have copied over the _document.js from the next styled components example and I am using the babel plugin from the styled components docs, but I am still getting the error.
_document.js
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
}
} finally {
sheet.seal()
}
}
}
.babelrc
{
"presets": [
"next/babel"
],
"plugins": [
[
"babel-plugin-styled-components"
]
]
}
Just started using next on wednesday so still fairly new with the technology, but I've looked through all the docs and this seems to be what is supposed to work, but I am still getting the error, any thoughts?
You aren't rendering() then returning () any jsx. That's your issue here most likely. You should also be importing Html, Head, Main, and NextScript from "next/document".
To resolve your issue, try refactoring as follows:
import Document { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
)
};
} finally {
sheet.seal()
}
}
render() {
return (
<Html lang='en'>
<Head>
<meta charSet='utf-8' />
{this.props.styles}
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
Then in _app.jsx you will import { ThemeProvider } from styled-components. ThemeProvider wraps the custom styled-components GlobalTheme JSX Element imported from another file. The following snippet is in typescript from a project I built a couple months ago. Most is relevant with the exception of the AppProps tidbit:
import Head from "next/head";
import { AppProps } from "next/app";
import { ThemeProvider } from "styled-components";
import { GlobalStyle, theme } from "../shared";
export default function App({ Component, pageProps }: AppProps) {
return (
<ThemeProvider theme={theme}>
<GlobalStyle theme={theme} />
<Head>
<title>Next.js</title>
</Head>
<main className="main">
<Component {...pageProps} />
</main>
</ThemeProvider>
)
}
I am using react navigation as per the docs but trying to make my app a bit more modular. I placed the result of createStackNavigator into a separate component..
Navigator.js
import React, { Component } from 'react';
import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import Home from './views/Home.js';
import TestComponent from './views/TestComponent.js';
const MainNavigator = createStackNavigator({
Home: {screen: Home},
Test: {screen: TestComponent}
});
export default createAppContainer(MainNavigator);
..and importing this component into my App.js
App.js
import React, { Component } from 'react';
import { View } from 'react-native';
import Header from './Header.js';
import Navigator from './Navigator.js';
import FooterMenu from './FooterMenu.js';
class App extends Component {
render() {
return (
<View>
<Header />
<Navigator />
<FooterMenu />
</View>
);
}
}
export default App;
My index.js is as follows:
import { AppRegistry } from 'react-native';
import App from './components/App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
Im finding that my <Header/> and <FooterMenu/> components are rendering but the <Navigator/> component is not.
I found that if I replace the top-level <View> component with a React fragment, it does render.
render() {
return (
<>
<Header />
<Navigator />
<FooterMenu />
</>
);
}
Although this syntax breaks my editor's (sublime) syntax highlighting. Though if I change the fragment to <React.Fragment> React native throws an exception.
My questions are:
Why does <Navigator/> not render if I wrap it in a <View> component?
Why do I get an error if I use <React.Fragment>?
If you want to create your own Navigator, here's how.
It is possible to take an existing Navigator and extend its behavior, using the following approach:
const MyStack = createStackNavigator({ ... });
class CustomNavigator extends React.Component {
static router = MyStack.router;
render() {
const { navigation } = this.props;
return <MyStack navigation={navigation} />;
}
}
Now it is possible to render additional things, observe the navigation prop, and override behavior of the router:
const MyStack = createStackNavigator({ ... });
class CustomNavigator extends React.Component {
static router = {
...MyStack.router,
getStateForAction: (action, lastState) => {
// check for custom actions and return a different navigation state.
return MyStack.router.getStateForAction(action, lastState);
},
};
componentDidUpdate(lastProps) {
// Navigation state has changed from lastProps.navigation.state to this.props.navigation.state
}
render() {
const { navigation } = this.props;
return (
<View>
<Header />
<MyStack navigation={navigation} />
<FooterMenu />
</View>
);
}
}
If you want to know more about this,
I am using material UI with Next.js. I am running onnpm run dev. My problem is that the styling on the site completely breaks whenever I press the reloading button on the browser. Is this normal behavior? Seems like Material-UI stops working.
Here is my code.
I have an index.js and a component.
index
import React, { Component } from 'react'
import CssBaseline from '#material-ui/core/CssBaseline';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import AppBar from '../components/NavBar/NavBar';
const theme = createMuiTheme({
palette: {
primary: {
main: '#f28411',
},
},
});
class App extends Component {
render() {
return (
<MuiThemeProvider theme={theme}>
<React.Fragment>
<CssBaseline />
<AppBar />
</React.Fragment>
</MuiThemeProvider>
)
}
}
export default App
component
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar'
import Toolbar from '#material-ui/core/Toolbar'
import Typography from '#material-ui/core/Typography'
class NavBar extends Component {
render() {
const { classes } = this.props;
return(
<div>
<AppBar position="static">
<Toolbar>
<Typography variant="title" color="inherit">
Test
</Typography>
</Toolbar>
</AppBar>
</div>
);
}
}
const styles = theme => ({
title: {
color: '#FFF',
},
});
NavBar.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(NavBar);
I had this same issue but was not using styled-components so the above answer did not apply, so I thought I would post in case this helps anyone else. To fix this, I just had to add the _document.js file under the pages/ directory included in the material-ui sample:
import React from 'react'
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { ServerStyleSheets } from '#material-ui/core/styles'
import theme from '../shared/utils/theme'
export default class MyDocument extends Document {
render() {
return (
<Html lang='en'>
<Head>
<meta name='theme-color' content={theme.palette.primary.main} />
<link
rel='stylesheet'
href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap'
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets()
const originalRenderPage = ctx.renderPage
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
}
}
It could be possible that you need a babel plugin for this.
In your package, add
npm install --save-dev babel-plugin-styled-components
In your .babelrc, add
{
"plugins": [
[ "styled-components", { "ssr": true, "displayName": true, "preprocess": false } ]
]
}
Let me know if this works.
As it turns out, you cannot use all of the third-party libraries for Next JS in the same way as you would use for React apps. You need to modify your pages/_document.js and pages/_app.js. Also, you will need theme.js for configuring Material UI colors and other default styles. You can include theme.js to any folder, in my case it is in helpers folder.
_app.js
import React from "react";
import App from "next/app";
import theme from '../helpers/theme'; // needed for Material UI
import CssBaseline from '#material-ui/core/CssBaseline';
import {ThemeProvider} from '#material-ui/core/styles';
class MyApp extends App {
static async getInitialProps({Component, ctx}) {
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
//Anything returned here can be accessed by the client
return {pageProps: pageProps};
}
render() {
const {Component, pageProps, router} = this.props;
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{/* default by next js */}
<Component {...pageProps}/>
</ThemeProvider>
)
}
}
export default MyApp
_document.js
import Document, {Html, Head, Main, NextScript} from "next/document";
import theme from '../helpers/theme';
import { ServerStyleSheets } from '#material-ui/core/styles';
import React from "react";
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
let props = {...initialProps};
return props;
}
render() {
return (
<Html>
<Head>
<meta name="theme-color" content={theme.palette.primary.main} />
</Head>
<body>
<Main/>
<NextScript/>
</body>
</Html>
);
}
}
MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and register rendering finish.
styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
};
};
theme.js
import { createMuiTheme } from '#material-ui/core/styles';
import { red } from '#material-ui/core/colors';
// Create a theme instance.
const theme = createMuiTheme({
palette: {
primary: {
main: '#FFF212',
},
secondary: {
main: '#FFF212',
},
error: {
main: red.A400,
},
background: {
default: '#fff',
},
},
});
export default theme;
There is an amazing repository in Github https://github.com/vercel/next.js/tree/canary/examples
There you can find a list of libraries with the correct way of using them in Next JS apps.
For your case here is the link: https://github.com/vercel/next.js/tree/canary/examples/with-material-ui.
I've been developing an idea but am getting stuck on something unusual (my brain hurts on react-router).
I am trying to dynamically render a list of items using .map from a returned object (of multiple similar objects) and appending them to the render(){return(<div />)}.
I just dont know another way than call a function then .map the result for this callback.
I think that the way I'm doing this means the rendered items lose context. The react-router <Link /> will function as expected in the normal flow (placed inside the render(){return(<div />)} ) but not when the item is created from outside of the render. I have posted the error below the code.
I have read Many different ways of getting around this using context and location/history and withRouter. Frankly I'm lost.
I would appreciate if someone could look at my example below and guide me in the right direction.
A few notes:
- main focus appears to be in mystuff
- i have many unnecessary imports i know
- stripped down for clarity, i would get lost otherwise
index
import _ from 'lodash';
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import { store, history } from './store';
import Main from './Main';
import { routyr } from './Menu';
// remaining paths in Menu.js (routyr) for menu visibility
const router = (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Main}>
{routyr}
</Route>
</Router>
</Provider>
)
render (router, document.getElementById('app'));
Main
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from './actionCreators';
import App from './app';
function mapStateToProps(state){
return{
info: state.info,
myProfile: state.myProfile
}
}
function mapDispatchToProps(dispatch){
return { actions: bindActionCreators(actionCreators, dispatch) }
}
const Main = connect(mapStateToProps, mapDispatchToProps)(App);
export default Main;
routyr
import React from 'react';
import { Link } from 'react-router';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import { store, history } from './store';
//pages
import App from './app';
import Landing from './Landing';
import Me from './mystuff';
import ViewStuff from './viewStuff';
//Routes for index.js
export const routyr = (
<span>
<IndexRoute component={Landing} />
<Route path="/myStuff" component={Me} />
<Route path="/viewStuff" component={ViewStuff} />
</span>
)
//Menu types
//loaded by app.js
export const menuLoggedIn = (
<div className="MainMenu">
<Link to='/' className="buttonA green">Home</Link>
<Link to='myStuff' className="buttonA green">My Stuff</Link>
</div>
);
export const menuLoggedOut = (
<div className="MainMenu">
<Link to='/login' className="buttonA green">Login</Link>
</div>
);
app
import React from 'react';
import _ from 'lodash';
import { Link } from 'react-router';
import auth from './auth';
import Landing from './Landing';
import Header from './Header';
import { menuLoggedIn, menuLoggedOut } from './Menu';
export default class App extends React.Component {
constructor(){
super();
this.state={
auth: auth.loggedIn(),
menu: null
};
}
componentWillMount(){
if (this.state.auth==true) {
this.setState({
menu: menuLoggedIn
})
}else{
this.setState({
menu: menuLoggedOut
});
}
}
render(){
return (
<div>
<Header />
{this.state.menu}<br />
<div id="view">
{React.cloneElement(this.props.children, this.props)}
</div>
</div>
);
}
};
mystuff
import React, { PropTypes } from 'react';
import { render } from 'react-dom';
import { Link } from 'react-router';
import { withRouter } from 'react-router';
import { Provider } from 'react-redux';
import * from './whacks';
export default class Me extends React.Component{
constructor(){
super();
}
componentDidMount() {
function listThem(oio){
oio.map(function(ducks){
render(
<div className="ListItem">
<Link to="/viewStuff"> _BROKEN_ View Stuff</Link>
<div className="listLabel">{ducks.type}</div>
<h3>{ducks.description.title}</h3>
{ducks.description.long}
</div>, document.getElementById('fishes').appendChild(document.createElement('div'))
);
});
}
var some = new Whacks();
some.thing(more, (close, open) => {
if(close){
console.log(close));
} else {
doIt(open);
}
});
}
render(){
return(
<div>
<Link to="viewStuff"> _WORKING_ View Stuff</Link>
<div id="fishes">
</div>
</div>
)
}
}
store
import { createStore, compose } from 'redux';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
/*-------ROOT REDUCER---------*/
/*-------DEFAULT STATES---------*/
/*-------CREATE STORE---------*/
/*-------INTEGRATE HISTORY---------*/
import me from './reducers/obj';
import myProfile from './reducers/myProfile';
const rootReducer = combineReducers(
{
routing: routerReducer,
me,
myProfile
}
);
//TEMP remove harcoded var
const uuidSet = "fa78d964";
export const defaultState = {
uuid: uuidSet,
};
export const store = createStore(rootReducer, defaultState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export const history = syncHistoryWithStore(browserHistory, store);
actionCreators
export function me (obj){
return {
type: "ADD_OBJECTLIST",
obj
}
}
export function myProfile (dump){
return {
type: "MY_DATA",
dump
}
}
from package.json
"react-redux": "^5.0.2",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.7",
"redux": "^3.6.0",
error
Uncaught Error: s rendered outside of a router context cannot navigate.
#UG,
I have tried the following in mystuff:
constructor(){
super();
this.state={
oio: {}
};
}
and
some.thing(more, (close, open) => {
if(close){
console.log(close));
} else {
this.setState({
oio: open
});
}
});
and
render(){
let flat = this.state.oio;
flat.map(function(ducks){
return (
<div className="ListItem">
<Link to="/viewStuff">View Stuff</Link>
<div className="listLabel">{ducks.type}</div>
<h3>{ducks.description.title}</h3>
{ducks.description.long}
</div>
)
})
}
and receive
Uncaught TypeError: flat.map is not a function
at Me.render
I am not sure if I get your issue completely. But I think you want to use Link inside render() method of myStuff
You can change that to following :
render(){
return(
<div>
<Link to="viewStuff"> _WORKING_ View Stuff</Link>
<div id="fishes">
{
oio.map(function(ducks){
return (
<div className="ListItem">
<Link to="/viewStuff"> _BROKEN_ View Stuff</Link>
<div className="listLabel">{ducks.type}</div>
<h3>{ducks.description.title}</h3>
{ducks.description.long}
</div>
);
}
</div>
</div>
)
}
As per the comment from James,
You should use react state to maintain oio object.
constructor() {
super();
//init
this.setState({oio : {}});
}
and update the state in async call, when state updates, component can be rerendered.
Huge thanks to UG_ for smacking me in the ear with state.
I have pulled in a component and created each components props from the callback objects.
My Working solution is as follows in mystuff:
constructor(props){
super(props);
this.state={
oio: []
}
}
componentDidMount() {
let listThem = (stuff) => {
let ioi = [];
stuff.forEach(function(dood, index, array) {
let lame = <MyItem plop={dood} key={index} />;
ioi.push(lame);
});
return (
this.setState({
oio: ioi
})
);
}
var some = new Whacks();
some.thing(more, (close, open) => {
if(close){
console.log(close));
} else {
listThem(open);
}
});
}
render(){
return(
<div>
{this.state.oio}
</div>
)
}
Which renders a new copy of the MyItem component with props from each returned object. So now my returned items contain context!