5

I'm new to Mobx and reactjs in general, I have knowledge in Redux and react native, and in Redux when I used to call an action and the props get updated, the componentDidUpdate life cycle method is triggered.

The scenario I'm having now is login. so the user fills the form, clicks submit, and the submit calls a Mobx action (asynchronous), and when the server responds, an observable is updated, and then it navigates to a main page (navigation happens in the component).

Here is my store code.

import { autorun, observable, action, runInAction, computed, useStrict } from 'mobx';

useStrict(true);

class LoginStore {
    @observable authenticated = false;
    @observable token = '';

    @computed get isAuthenticated() { return this.authenticated; }


    @action login = async (credentials) => {
        const res = await window.swaggerClient.Auth.login(credentials)l
        // checking response for erros
        runInAction(() => {
            this.token = res.obj.token;
            this.authenticated = true;
        });
    }
}

const store = new LoginStore();

export default store;

export { LoginStore };

and this handler is in my component.

handleSubmit = (e) => {
        e.preventDefault();

        this.props.form.validateFields((err, values) => {
            if (!err) {
                this.props.store.login(values);
            }
        });
    }

    componentDidUpdate() {
        if (this.props.store.isAuthenticated) {
            const cookies = new Cookies();
            cookies.set('_cookie_name', this.props.store.token);
            this.props.history.push('main');
        }
    }

It's not the ideal code, I'm just experimenting, but I'm not quite getting it.

Also, if I use the computed value (isAuthenticated) in the render life cycle method, the componentDidUpdate is triggered, but if I didn't use it in the render method, the componentDidUpdate is not triggered. For example, if I do this

render() {
  if (this.props.store.isAuthenticated) return null
  // .... rest of the code here
}

the above will trigger the componentDidUpdate.

Am I missing something? is there a better way to do it with Mobx? Thanks

David Schumann
  • 13,380
  • 9
  • 75
  • 96
Ibraheem Al-Saady
  • 854
  • 3
  • 14
  • 30
  • An observer React Component will re-render when the observables that got de-referenced in the last render are changed. Maybe it would be better to put this logic separated from the view? – Tholle Oct 07 '17 at 18:21
  • 1
    @Tholle yeah definitely the logic should be separated from the view, I'm just experimenting with mobx, and I wanted to know about this case specifically. Thanks for the suggestion :) – Ibraheem Al-Saady Oct 07 '17 at 19:10

2 Answers2

11

Observer component will only react to observables referred in its render method. MobX documentation covers this.

I would recommend you to use when to solve the problem.

componentDidMount() {
  when(
    () => this.props.store.isAuthenticated,
    () => { 
      // put your navigation logic here
    }
  );
}
David Schumann
  • 13,380
  • 9
  • 75
  • 96
Konstantin
  • 24,271
  • 5
  • 48
  • 65
  • 1
    it's the same problem, it's not triggering unless the render function has the `this.props.store` and if the render function has it, the `componentDidUpdate` is triggered. what could be wrong here? – Ibraheem Al-Saady Oct 07 '17 at 10:02
  • oh thanks for the explanation, and that worked as I wanted. is `when` safe to use in production though? – Ibraheem Al-Saady Oct 07 '17 at 19:09
  • @IbraheemAl-Saady what do you mean? We use `when` in our production code and it works as it should. I don't understand what kind of safety you are talking about. – Konstantin Oct 08 '17 at 10:46
  • 1
    @Alik it shows an error , when is not defined. How to import it? – Yerlan Yeszhanov Apr 09 '19 at 06:58
1

Mobx suggests the following solutions for such a case:

  1. when
  2. autorun
  3. reaction

See the examples below, and don't forget to dispose:

componentDidMount() {
    this.disposers.push(

            // option with autorun:

            autorun(() => {
                this.runYourLogicHere();
            })

            // another option with reaction:

            reaction(
                () => this.yourModelOrProps.something,
                () => {
                    this.runYourLogicHere();
                }
            )

    )
}
...

componentWillUnmount() {
    this.disposers.forEach(disposer => {
        disposer();
    });
}

And see the answer of @Dominik Serafin in parallel thread as a reference.

kli
  • 456
  • 3
  • 12