Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Works on development but fails on production in Gatsby #44

Open
abhagsain opened this issue Jan 19, 2020 · 3 comments
Open

Works on development but fails on production in Gatsby #44

abhagsain opened this issue Jan 19, 2020 · 3 comments

Comments

@abhagsain
Copy link

I've been trying to fix this issue but couldn't. I'm using use-dark-mode with gatsby and while developing the application it works fine, but when I run build of the application it shows wrong behaviour, On page refresh the icon changes from the previous value even though the mode (dark/light persists)

darkmode
I've added the your custom js in my html but doesn't seem to work.
Here's my implimentation

export default function Navbar({ title }) {
  const darkMode = useDarkMode()
  return (
    <nav className="nav">
      <Container>
        <div className="nav__content">
          <Link to="/" className="nav__brand">
            {darkMode.value ? "Dark" : "Light"}
          </Link>
          <div className="nav__right">
            
            {darkMode.value === true ? (
              <Sun disable={darkMode.disable} />
            ) : (
              <Moon enable={darkMode.enable} />
            )}
            
          </div>
        </div>
      </Container>
    </nav>
  )
}

This is how I style elements

body.light-mode nav {
  background: rgb(153, 153, 153);
  color: black;
}

body.dark-mode nav {
  background: $dark-mode-section;
}

Thanks for your help

@abhagsain abhagsain changed the title Works on development mode but fails on production mode in Gatsby Works on development but fails on production in Gatsby Jan 19, 2020
@ognus
Copy link

ognus commented Jul 12, 2020

Had the same problem.

Turned out to be an issue with Gatsby and how React SSR works. See discussion here:
gatsbyjs/gatsby#17914

TLDR:

You can encounter it if server-rendered (or built) content is different from client content on initial load. Gatsby re-hydrates the elements on the initial load, but this is not always enough and some components will show the server state.

The proposed solution of forcing re-render worked for me (two-pass rendering):

class Layout extends React.Component {
  constructor(props) {
    super(props)
    this.state = { isClient: false }
  }
  render() {
    // can be `div` or anything else, I tried to keep this generic
    return <React.Fragment key={this.state.isClient}> 
      {/*...*/}
    </React.Fragment/>
  }
  componentDidMount() {
    this.setState({ isClient: true })
  }
}

For my use case, conditionally rendering the toggle element worked the best:

export const ThemeToggle = () => {
  const darkMode = useDarkMode(false)

  const [isClient, setIsClient] = useState(false)

  useEffect(() => {
    setIsClient(true)
  }, [])

  return (
    <Container>
      {isClient && (
        <Switch
          checked={darkMode.value}
          onChange={darkMode.toggle}
        />
      )}
    </Container>
  )
}

It's not perfect since the user sees the render, but it's good for now :)

@kelvindecosta
Copy link

@ognus Thank you so much for this!

@astefanutti
Copy link

FWIW, using a post body script in Gatsby SSR, to update the input state, prevents the users from seeing the render every time.

Here is the whole code: astefanutti/website@f315e93.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants