Why does the component is not re-rendering when i add @action decorator to an react event handler function

130 Views Asked by At

Problem:

The below react component is not re-rendering when I add @action decorator to the class member fn.

import { observable, action } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import { render } from "react-dom";

@observer
class PriceCounter extends React.Component {
  @observable price = 0;

  @action
  setPrice = () => {
    this.price = this.price + 1;
  };

  @action
  currentPrice = () => {
    return this.price;
  }

  render() {
    return (
      <div>
        <span className="text-3xl">Total: {this.currentPrice()}</span>
        <button onClick={this.setPrice}>Change details</button>
      </div>
    );
  }
}

render(<PriceCounter />, document.getElementById("root"));

  • The PriceCounter component re-renders as expected when @action is removed from currentPrice fn.

Why? Explanation needed.

3

There are 3 best solutions below

0
Prasanth On BEST ANSWER

Actions should only, and always, be used on functions that modify state. Have a look at When to use actions

In the example you mentioned, currentPrice method is not modifying the state. So you need not use action decorator to that.

2
chandan_kr_jha On

Either you can use this.price to show the updated value of price. Or you can use computed decorator to do this.

@observer
class PriceCounter extends React.Component {
  @observable price = 0;

  @action
  setPrice = () => {
    this.price = this.price + 1;
  };

  @computed
  get currentPrice() {
    return this.price;
  }

  render() {
    return (
      <div>
        <span className="text-3xl">Total: {this.currentPrice}</span>
        <button onClick={this.setPrice}>Change details</button>
      </div>
    );
  }
}

or

@observer
class PriceCounter extends React.Component {
  @observable price = 0;

  @action
  setPrice = () => {
    this.price = this.price + 1;
  };

  @action
  currentPrice = () => {
    return this.price;
  };

  render() {
    return (
      <div>
        <span className="text-3xl">Total: {this.price}</span>
        <button onClick={this.setPrice}>Change details</button>
      </div>
    );
  }
}
0
Chinmaya On

This is a wrong usage of action.

@action is expected to be wrapped around a function in which observables are changed. Here, you are not changing any observables in currentPrice function.

Since render is a tracked function in mobx-react, if you are not wrapping currentPrice function with action & if you call this.currentPrice() mobx tracks observables used in that function.

If you are wrapping functions used in a tracked function with action mobx doesn't know if you intend to track observables or do you want to run tracked functions after the function is executed.