Search code examples
reactjstypescriptmobx

component not re-rendering when updating the state in mobx


I'm trying to understand why my app is not re rendering when I changing my state in the mobx observable names array. I'm changing the value with the input tag. would love for some help :)

observers component:

import {observable, action, autorun, computed} from 'mobx'

class TodosStore {
    @observable names = ["p1", "p2", "p3"]
    @observable filter = ""

    @action
    get filterredValue(){
        return store.names.filter(word => word.includes(this.filter))
    }
}

//@ts-ignore
var store = window.store = new TodosStore

export default store


autorun(() => {
    console.log(store.filter); 
    console.log(store.names); 
})

and this is my app component:

import React from 'react';
import './App.css';
import store from  './components/observers'

class App extends React.Component {
  constructor(props :any) {
    super(props);
    this.setName = this.setName.bind(this);
  }

  setName = (e : any) => {
    store.filter = e.target.value

  }

  render() {
  return (
    <div className="App">
      <header className="App-header">
        {store.filterredValue.map((name) => <li key={name}>{name}</li>)}
        <input
          onChange={(e) => this.setName(e)}
          />
      </header>
    </div>
  );
}
}

export default App;

Solution

  • You can define things like observable, computed and action in TodoStore using makeObservable as decorators are currently not being preferred (decorators are currently not an ES standard, and the process of standardization is taking a long time):

    TodoStore:

    import { observable, autorun, computed, makeObservable, action } from "mobx";
    
    class TodoStore {
      names = ["p1", "p2", "p3"];
      filter = "";
    
      constructor() {
        makeObservable(this, {
          names: observable,
          filter: observable,
          filterredValue: computed,
          setFilter: action,
        });
        autorun(() => {
          console.log(this.filter);
          console.log(this.names);
        });
      }
    
      get filterredValue() {
        return this.names.filter((word) => word.includes(this.filter));
      }
    
      setFilter(filter) {
        this.filter = filter;
      }
    }
    
    export const todoStore = new TodoStore();
    

    And, here is App component using observer (A higher order component which makes a functional or class based React component re-render when observables change):

    App:

    import { observer } from "mobx-react";
    import { Component } from "react";
    
    class App extends Component<any> {
      setName = (e: React.ChangeEvent<HTMLInputElement>) => {
        this.props.store.setFilter(e.target.value);
      };
    
      render() {
        return (
          <div className="App">
            <header className="App-header">
              {this.props.store.filterredValue.map((name) => (
                <li key={name}>{name}</li>
              ))}
              <input onChange={(e) => this.setName(e)} />
            </header>
          </div>
        );
      }
    }
    
    export default observer(App);
    

    Demo:

    Edit my-mobx-react