mobx-persist icon indicating copy to clipboard operation
mobx-persist copied to clipboard

Hydration didn't complete before componentDidMount

Open dstuff opened this issue 5 years ago • 1 comments

I've got an empty store on componentDidMount so I can't render data on component loading. As I can see via logs store hydrated just after the component was mounted.

I've tried to implement solution from https://github.com/pinqy520/mobx-persist/issues/38 on my root component (App.js) but if user not on index page then componentDidMount with hydration didn't trigger.

import {store} from '../../state';

import Header from '../header';
import Footer from '../footer';
import ErrorBoundry from '../error-boundry';

import './app.css';
import MainPage from '../main-page';
import LoginPage from '../login-page';
import AccountPage from '../account-page';

import {create} from 'mobx-persist';

const hydrate = create({
  storage: localStorage,
  jsonify: true,
});

@observer
class App extends Component {

  async componentDidMount() {
    await hydrate('store', store);
    console.log('Mounted index page');
  }

  SearchPage = () => <h2>Search Page</h2>;
  AddListingPage = () => <h2>Add Listing Page</h2>;

  render() {
    return (
      <Provider store={store}>
        <ErrorBoundry>
          <Router>
            <div className="app-container">
              <Header />
               <div className="content-wrapper">
                 <Route path="/" exact component={MainPage}/>
                 <Route path="/search" component={this.SearchPage}/>
                 <Route path="/account" component={AccountPage}/>
                 <Route path="/login" component={LoginPage}/>
                 <Route path="/add-listing" component={this.AddListingPage}/>
               </div>
              <Footer />
            </div>
          </Router>
        </ErrorBoundry>
      </Provider>
    );
  }
}

For example, in AccountPage store is empty on componentDidMount. Only way I can check if user logged-in is adding check to componentDidUpdate.


...

@inject('store')
@withRouter
@observer
class AccountPage extends Component {

  state = {
    user: Object.assign({}, this.props.store.user.user),
  };

  componentDidMount() {
    console.log('Account', this.props.store.user);
  }

  componentDidUpdate() {
    const {store, history} = this.props;
    if(!store.auth.isLogged) {
      history.push('/login');
    }
  }

  ...
  render() {
  <div>
    {this.props.store.user.user.firstName}
    {this.props.store.auth.isLogged}
  </div>
}
}

Any suggestions on how to get a hydrated store before component will be mounted on any page? I'm using react-router

dstuff avatar Mar 27 '19 12:03 dstuff

Hey, I had a similar issue, where my APIs were getting called from cDM while the hydration was not complete. hence some empty headers and params were being passed. So what I did was, I created a component called PersistStore that had the responsibility of hydrating all my stores and after all of them were hydrated only then I would render my main app. Here is the code.

const rootStore = new RootStore();
constructor(props: Props) {
    super(props);

    const p1 = create()('albumsModel', rootStore.albumsModel);
    const p2 = create()('userModel', rootStore.userModel);
    const p3 = create()('imagesModel', rootStore.imagesModel);
    const p4 = create()('contextStore', rootStore.context);
    
    Promise.all([p1, p2, p3, p4])
	.then(() => {
              this.setHydration(true);
	})
	.catch(err => {
		this.setHydration(true);
		throw err;
	});
}

@action('Set Hydration flag')
setHydration(x: boolean) {
	this.isHydrationComplete = x;
}

render() {
	if (!this.isHydrationComplete) return null;
	return this.props.render(rootStore.getStores());
}

And this would be a wrapper for your main app. I am using render props pattern but you could easily use hooks.

YashGadle avatar Apr 11 '19 10:04 YashGadle