Skip to content

Works on development but fails on production in Gatsby #44

Description

@abhagsain

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

Activity

  1. changed the title [-]Works on development mode but fails on production mode in Gatsby[/-] [+]Works on development but fails on production in Gatsby[/+] on Jan 19, 2020
  2. ognus commented on Jul 12, 2020

    @ognus

    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 :)

  3. kelvindecosta commented on Jan 25, 2021

    @kelvindecosta

    @ognus Thank you so much for this!

  4. astefanutti commented on Jan 26, 2021

    @astefanutti

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions