3

I just switched out this.setState to use mobx observable, because I have multiple GET requests that fetch data. This prevents the PieChart from being re-rendered every time this.setState is called.

However, now the child component does not ever get re-rendered and stays with the initial placeholder mobxState. How can I get the PieChart child component to re-render when the data for it comes in from the API.

class Device extends React.Component {
  mobxState = {
    customOptions: [],
    rowData: []
  };

  //mount data
  componentDidMount() {
    //call the data loader
    this.fetchData();
  }

  fetchData = () => {
    axios
      .get("/custom_options.json")
      .then(response => {
        this.mobxState.customOptions = response.data.custom_options;
      })
      .then(
        //data for PieChart, need this portion to render the PieChart
        axios.get("/devices.json").then(response => {
          this.mobxState.rowData = response;
        })
      );
  };

  render() {
    return <PieChart data={this.mobxState.rowData} />;
  }
}

decorate(Device, {
  mobxState: observable
});

export default Device;
Tholle
  • 108,070
  • 19
  • 198
  • 189
GavinBelson
  • 2,514
  • 25
  • 36

1 Answers1

2

You need to make sure your Device component is an observer, and if you are using a MobX version below 5 you have to slice() or peek() the array in the render method.

import { observer } from "mobx-react";

class Device extends React.Component {
  // ...

  render() {
    return <PieChart data={this.mobxState.rowData.slice()} />;
  }
}

decorate(Device, {
  mobxState: observable
});

export default observer(Device);
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • I'm using mobx 5.0.3. If I try to use .peek() it errors with: `this.mobxState.rowData.peek is not a function` – GavinBelson Jul 24 '18 at 20:50
  • Using the `observer(Device)` yields a ReferenceError: observer is not defined – GavinBelson Jul 24 '18 at 20:51
  • @HoosierCoder You of course have to import it first. – Tholle Jul 24 '18 at 20:53
  • My import statement is: `import { decorate, observable, computed, action } from "mobx"` – GavinBelson Jul 24 '18 at 20:53
  • `observable` is imported, but not _observer_ – kingdaro Jul 24 '18 at 20:58
  • Also @Tholle Is the extra .slice() actually necessary? I have a feeling the component would re-render just with `this.mobxState.rowData` – kingdaro Jul 24 '18 at 21:00
  • Added `import { observer } from "mobx-react"` - no error but it still doesn't re-render the `PieChart` – GavinBelson Jul 24 '18 at 21:02
  • @kingdaro `this.mobxState.rowData` is not dereferencing anything in the array, so depending on the implementation of `PieChart` the observer might not know to re-render `Device` if something in `rowData` changes. – Tholle Jul 24 '18 at 21:02
  • @HoosierCoder What happens if you write `this.mobxState.rowData.replace(response);` instead? – Tholle Jul 24 '18 at 21:09
  • In the .then part of the http request it errors with: `_this.mobxState.rowData.replace is not a function` – GavinBelson Jul 24 '18 at 21:15
  • Also, if I trigger an additional child component that uses this.state, it will correctly re-render all components. Is there maybe a good way to manually trigger the render in `.then()` of an API call? – GavinBelson Jul 24 '18 at 21:20
  • @HoosierCoder If `rowData` doesn't have `replace`, then it's most likely not a proper MobX array. There might be something else strange going on. – Tholle Jul 24 '18 at 21:21
  • To anyone else reading this.. my specific non-render is probably on the stack level. Using rails 5.0.2, webpacker, react-rails gem – GavinBelson Jul 24 '18 at 23:35