Search code examples
javascriptreactjsreduxsession-storage

Why is redux-sessionstorage not updating my node's attributes when restoring state?


I use redux-sessionstorage to store a subset of my app's state to session storage. Part of that state indicates whether certain panels in the UI are expanded or collapsed (visible or hidden).

I have a sample app reproducing exactly this: https://github.com/shawninder/sessionstorage-bug

See the readme for complete reproduction steps.

Also hosted online here (give it a moment to unfreeze)

The problem happens when I reload the page. redux-sessionstorage correctly restores the previous state from sessionstorage and triggers a render, yet the style attribute I'm using to do the hiding and showing is not updated.

In other words, this works as expected:

<p>showNotice: {props.app.showNotice ? 'true' : 'false'}</p>

I can see showNotice: false on page load, but it's then immediately replaced by showNotice: true when redux-sessionstorage restores the saved state.

But the following doesn't work:

<div
  className='notice'
  style={{
    opacity: props.app.showNotice ? 1 : 0,
    transform: props.app.showNotice ? 'translateX(0)' : 'translateX(-100%)'
  }}
>
  Notice!
</div>

The page loads with the notice hidden (as expected), but it doesn't get shown when redux-sessionstorage restores the state. In fact, it doesn't show until I click the toggle button twice, the first time to set showNotice back to false, and then a second time to set it to true and show the <div>.

Why is redux-sessionstorage not updating the style attribute here, and how do people generally handle this?


Solution

  • @lecstor pointed to the right thread about this issue.

    This is a thing with Server Side Rendering (SSR). The workaround Dan Abramov described on this comment solved the issue.

    You're using Next.js for SSR. When running your project on dev mode, you can see the following error message on Chrome console, after pressing Toggle to true and reloading:

    Warning: Text content did not match. Server: "false" Client: "true"

    Since Next doesn't know anything about the saved state on the client side, it always returns app.showNotice to false. You can also check the returned content for GET localhost on Chrome Network Tab.

    Apparently Next.js tries to merge the content returned from the server and the one rendered locally, but fails to merge the style. Apparently, it's an expected behaviour on Next and also on React 16 SSR, that you can overcome by triggering a second render() or doing the implementation below.

    class App extends Component {
    
      constructor(props) {
        super(props);
        this.state = { showNotice: false };
      }
    
      componentDidMount() {
        this.setState({ showNotice: this.props.app.showNotice });
      }
    
      componentWillReceiveProps(props) {
        this.setState({ showNotice: props.app.showNotice });
      }
    
      render() {
        return (
          <div>
            <p>showNotice: {this.state.showNotice ? 'true' : 'false'}</p>
            <button onClick={(event) => {
              this.props.dispatch({ type: 'App:toggleNotice' })
            }}>toggle</button>
            <div
              className='notice'
              style={{
                opacity: this.state.showNotice ? 1 : 0,
                transform: this.state.showNotice ? 'translateX(0)' : 'translateX(-100%)'
              }}
            >
              Notice!
            </div>
            <style jsx>{`
              .notice {
                width: 100px;
                transition-property: opacity, transform;
                transition-duration: 0.4s;
              }
            `}</style>
          </div>
        )
      }  
    }