nghiepdev / next-mobx-wrapper

[DEPRECATED] Mobx wrapper for Next.js
MIT License
12 stars 2 forks source link

Change not triggering after async change in _app.getInitialProps() #6

Open iyobo opened 5 years ago

iyobo commented 5 years ago

On the server

In getInitialProps(), after the line await userStore.me();, a change happens in the userStore with a value set. (async call to backend server to grab profile information and store it in userStore)

I verify that the value is set using a breakpoint after that line.

However in the same server-side, when it gets to the render function, there is no profile information in userStore.

Breakpoints tell me that await userStore.me(); and the profile data gets set BEFORE the render function gets called, so its is not a matter of async mis-order.

//CustomApp
import React from 'react';
import App, {Container} from 'next/app';

import ErrorPage from 'next/error';
import Router from 'next/router';
import {Provider, useStaticRendering} from 'mobx-react';
import axios from 'axios';
import {isServer} from '../utils/appUtils';
import {withMobx} from 'next-mobx-wrapper';
import * as stores from '../stores';
import NProgress from 'nprogress';

axios.defaults.baseURL = process.env.baseUrl;

useStaticRendering(isServer);

Router.events.on('routeChangeStart', () => {
    NProgress.start();
});
Router.events.on('routeChan' +
    'geComplete', () => {
    NProgress.done();
});
Router.events.on('routeChangeError', () => {
    NProgress.done();
});

@withMobx(stores)
export default class CustomApp extends App {

    static async getInitialProps({Component, ctx}) {
        console.log('_app.js...');

        // in server mode, set cookies to be used for store async initializations
        if (isServer) {
            const serverCookies = ctx.req.headers.cookie;
            // console.log('We should not be seeing this in the client');
            if(serverCookies)
                axios.defaults.headers.cookie = serverCookies;
        }

        //load user profile
        const userStore = stores.getUserStore();
        await userStore.me();

        let pageProps = {};
        if (typeof Component.getInitialProps === 'function') {
            pageProps = await Component.getInitialProps(ctx);
        }

        return {
            pageProps
        };
    }

    render() {

        const {Component, pageProps, store} = this.props;
        const { statusCode } = pageProps;

        if (statusCode && statusCode >= 400) {
            return <ErrorPage statusCode={statusCode} />;
        }

        return (
            <Container>
                <Provider store={store}>
                    <Component {...pageProps}  />
                </Provider>
            </Container>
        );
    }
}
iyobo commented 5 years ago

This is my UserStore

import {action, observable} from 'mobx';
import {getAvatar} from '../utils/imageUtils';
import {AuthMode} from '../components/auth/AuthComponent';
import {BaseStore, getOrCreateStore} from 'next-mobx-wrapper';
import {getUIStore} from './UIStore';

const axios = require('axios');

interface User {
    picture: string;
    id: string;
    firstName: string;
    lastName: string;
    email: string;
    phone?: string;
}

export class UserStore extends BaseStore {

    @observable currentUser: User;
    @observable isShowingAuthModal: boolean = false;
    @observable authMode: string = AuthMode.LOGIN;
    ...

    /**
     * Call this in the root store init function
     */
    async me() {
        const uiStore  = getUIStore();
        uiStore.incrementLoadCount();
        const res = await axios.get('/api/v1/users/me');
        uiStore.decrementLoadCount();
        this.currentUser = res.data;
    }
...
}

export const getUserStore = getOrCreateStore('userStore', UserStore);
nghiepdev commented 5 years ago

Hi @iyobo You can try changing this:

  //load user profile
  const userStore = stores.getUserStore();
  await userStore.me();

to

  const {userStore} = ctx.store;

  //load user profile
  await userStore.me();
iyobo commented 5 years ago

@nghiepit Still doesn't work.

nghiepdev commented 5 years ago

Maybe the problem is async/await. try:

runInAction(() => {
   this.currentUser = res.data;
})

I recommended flows instead.

iyobo commented 5 years ago

I doubt that's the issue. strict mode is off. Action encapsulation not needed.