Next.js: Opted-out of Automatic Static Optimisation warning when no GIP present in _app.js

Created on 2 Nov 2019  路  4Comments  路  Source: vercel/next.js

Bug report

Describe the bug

Getting the warning "You have opted-out of Automatic Static Optimization due to getInitialProps in pages/_app." However there isn't any getInitialProps in our custom _app.js

import React from 'react'
import App from 'next/app'

// REDUX imports
import { Provider } from 'react-redux'
import withReduxStore from './../src/redux/with-redux-store'

// CONTEXT imports
import DeviceTypeContextProvider from './../src/contexts/device-type-context-provider';

// LOCAL imports
import Layout from './../src/components/common/layout'

class MyApp extends App {

  render() {
    const { Component, pageProps, reduxStore } = this.props
    return (
      <Provider store={reduxStore}>
        <DeviceTypeContextProvider>
          <Layout>
            <Component {...pageProps} />
          </Layout>
        </DeviceTypeContextProvider>
      </Provider>
    )
  }
}

export default withReduxStore(MyApp)

To Reproduce

Expected behavior

No warnings when no GIP is present in custom _app.js

Screenshots

Screenshot 2019-11-02 at 12 50 49 PM

System information

  • Version of Next.js: 9.1.2

Additional context

Not sure what is triggering this warning, as in the error description
Could this also be the reason the static files are getting 404ed?
Screenshot 2019-11-02 at 1 12 59 PM

Most helpful comment

Actually there is a solution for this. When I worked on the examples/with-redux I thought about this and added an undocumented ssr option to the withRedux hook 馃榿.

Example

First check out the examples/with-redux. (Other redux example might not have this option, yet.)
Then apply the following changes to examples/with-redux/pages/index.js:

import React from 'react'
import { useDispatch } from 'react-redux'
import { withRedux } from '../lib/redux'
import useInterval from '../lib/useInterval'
import Clock from '../components/clock'
import Counter from '../components/counter'

const IndexPage = () => {
  // Tick the time every second
  const dispatch = useDispatch()
  useInterval(() => {
    dispatch({
      type: 'TICK',
      light: true,
      lastUpdate: Date.now()
    })
  }, 1000)
  return (
    <>
      <Clock />
      <Counter />
    </>
  )
}

- IndexPage.getInitialProps = ({ reduxStore }) => {
-   // Tick the time once, so we'll have a
-   // valid time before first render
-   const { dispatch } = reduxStore
-   dispatch({
-     type: 'TICK',
-     light: typeof window === 'object',
-     lastUpdate: Date.now()
-   })
-   return {}
- }
-
- export default withRedux(IndexPage)

+ export default withRedux(IndexPage, {
+   ssr: false
+ })

Explanation

We disabled redux on the server-side here. This means your redux store will only have it's initial state when Next.js renders your components on the server. As soon as the client hydrated on the client, your app will get the right state.

Comparison

| ssr: true | ssr: false |
|------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
| ssr-true | ssr-false |
| automatic static optimization: disabled | automatic static optimization: enabled |

Thanks to Next.js you as a developer can now decide, what page of yours should use which strategy.

Please note that every strategy has it's ups and downsides.

Bonus

The withApollo example has the same option 馃.

All 4 comments

So, after digging up more I realised there is a GIP present in the withReduxStore HOC. My bad.

So is there no way to get nextjs with redux working and get Automatic Static Optimisation? I don't see a way to remove getInitialProps from the code below and still get redux to work,

Ready to close the issue but still looking for some feedback/insight on this.

with-redux-store.js

import React from 'react'
import { initializeStore } from './store'

const isServer = typeof window === 'undefined'
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'

function getOrCreateStore (initialState) {
  // Always make a new store if server, otherwise state is shared between requests
  if (isServer) {
    return initializeStore(initialState)
  }

  // Create store if unavailable on the client and set it on the window object
  if (!window[__NEXT_REDUX_STORE__]) {
    window[__NEXT_REDUX_STORE__] = initializeStore(initialState)
  }
  return window[__NEXT_REDUX_STORE__]
}

export default App => {
  return class AppWithRedux extends React.Component {
    static async getInitialProps (appContext) {
      // Get or Create the store with `undefined` as initialState
      // This allows you to set a custom default initialState
      const reduxStore = getOrCreateStore()

      // Provide the store to getInitialProps of pages
      appContext.ctx.reduxStore = reduxStore

      let appProps = {}
      if (typeof App.getInitialProps === 'function') {
        appProps = await App.getInitialProps(appContext)
      }

      return {
        ...appProps,
        initialReduxState: reduxStore.getState()
      }
    }

    constructor (props) {
      super(props)
      this.reduxStore = getOrCreateStore(props.initialReduxState)
    }

    render () {
      return <App {...this.props} reduxStore={this.reduxStore} />
    }
  }
}

Actually there is a solution for this. When I worked on the examples/with-redux I thought about this and added an undocumented ssr option to the withRedux hook 馃榿.

Example

First check out the examples/with-redux. (Other redux example might not have this option, yet.)
Then apply the following changes to examples/with-redux/pages/index.js:

import React from 'react'
import { useDispatch } from 'react-redux'
import { withRedux } from '../lib/redux'
import useInterval from '../lib/useInterval'
import Clock from '../components/clock'
import Counter from '../components/counter'

const IndexPage = () => {
  // Tick the time every second
  const dispatch = useDispatch()
  useInterval(() => {
    dispatch({
      type: 'TICK',
      light: true,
      lastUpdate: Date.now()
    })
  }, 1000)
  return (
    <>
      <Clock />
      <Counter />
    </>
  )
}

- IndexPage.getInitialProps = ({ reduxStore }) => {
-   // Tick the time once, so we'll have a
-   // valid time before first render
-   const { dispatch } = reduxStore
-   dispatch({
-     type: 'TICK',
-     light: typeof window === 'object',
-     lastUpdate: Date.now()
-   })
-   return {}
- }
-
- export default withRedux(IndexPage)

+ export default withRedux(IndexPage, {
+   ssr: false
+ })

Explanation

We disabled redux on the server-side here. This means your redux store will only have it's initial state when Next.js renders your components on the server. As soon as the client hydrated on the client, your app will get the right state.

Comparison

| ssr: true | ssr: false |
|------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
| ssr-true | ssr-false |
| automatic static optimization: disabled | automatic static optimization: enabled |

Thanks to Next.js you as a developer can now decide, what page of yours should use which strategy.

Please note that every strategy has it's ups and downsides.

Bonus

The withApollo example has the same option 馃.

Thank you so much @HaNdTriX for the detailed response.

So, to get the redux working with Automatic Static Optimization I'll have to let go of SSR. That's some trade off. Will try both approaches and see which one makes more sense to us. Thanks again.

Also do you this static files getting 404ed is somehow connected to this?
Screenshot 2019-11-02 at 7 52 00 PM

Closing as the initial issue appears to resolved

Was this page helpful?
0 / 5 - 0 ratings