2
votes

Using a route that needs props passed in to the components, I'm passing in a boolean that is observable from my store. When this value changes, components rendered by the route are not updated.

<Switch>
    <Route exact path="/" render={()=><MyComponent isLoading={this.props.MyMobxStore.ShowMyComponentLoader} />} />
</Switch>

This component is Observable and injected with MyMobxStore. If I change that observable, the component "MyComponent" is not re-rendered or notified about the change.

If I call any arbitrary "setState" on my top level component, it works. or if I add a dummy element such as

<div idk={this.props.MyMobxStore.ShowMyComponentLoader}/>

that makes the current component re-render such that "MyComponent" that depends on this observable behaves as expected.

Is there an obvious solution that I'm missing? I can add one of these hacks to make it work. I could wrap my "MyComponent" in another observable and pass it down that way, But that isn't conducive with my design. Though It doesn't matter that much. This is purely academic at this point. I'd like to know why it doesn't work.

Update

was asked to include more code.

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
import {inject, observer } from 'mobx-react';
import {MyComponent, CoolChildComponent} from '.coomComponents';

@inject('LoaderStore')
@observer
class App extends Component {
    
    componentWillMount() {
        this.props.LoaderStore.setShowLocalLoader(true);
        setTimeout(() => {
            this.props.LoaderStore.setShowLocalLoader(false);
        }, 5000);
    }
    

    render() {
        return( 
            <div>
            {/* note router container and "with router" wrapper one level up. */}
              <Switch>
                  <Route render={()=>{return <MyComponent isLoading={this.props.LoaderStore.showLocalLoader}><CoolChildComponent/></MyComponent>}} />
              </Switch>
            </div>
        );
    }
}

export default App;

note, I know ways to get around this. What I don't understand is why MobX doesn't realize that render is using LoaderStore.showLocalLoader

2

2 Answers

3
votes

try the following:

render() {
    const showLocalLoader = this.props.LoaderStore.showLocalLoader
    return( 
        <div>
        {/* note router container and "with router" wrapper one level up. */}
          <Switch>
              <Route render={()=>{return <MyComponent isLoading={showLocalLoader}><CoolChildComponent/></MyComponent>}} />
          </Switch>
        </div>
    );
}

this will cause the correct value to be sent as prop to the Route, there is an issue that sometimes the change in value doesn't trigger re-rendering when the observable is nested inside the return value of render.

let me know if it worked

0
votes

sounds like you are missing an observer wrapper for MyComponent you can use @observer if you have support for decorators else you should enable decorators as shown here,

hopes that helps, if that's the issue here is a little explanation:

when you set an observable in a mobx store you can react to a change of this observable in a few ways (i'm just listing 2 for more options see the mobx docs):

  1. using autorun - more info
  2. using @observable - more info

both this methods work on the same principles, just that in the option of observable what happens is that the render function works like autorun works

it's pretty straight forward if you have an observable inside the function it will trigger it every time the observable in the function scope changes

good luck!