Search code examples
javascriptreactjstypescriptmobxmobx-react

Change @observable property don't rerender observer component in my project with mobx


I have a problem with Mobx in my project. Changing property in my store by action don't fire rerendering component. I annotated the @action, the field with @observer and component with HoC observer.

I did a minimal setup to represent the problem on StackBlizt : https://stackblitz.com/edit/react-ts-dwqnm5?file=TestComponent.tsx

After click button Token should change.

enter image description here

Index.tsx

function App() {
  return (
    <StoreProvider store={new UserStore()}>
      <div>
        <h1>Hello REACT!</h1>
        <TestComponent />
      </div>
    </StoreProvider>
  );
}

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

StoreContext.tsx

export const StoreContext = createContext<UserStore>({} as UserStore);

export type StoreComponent = FC<{
  store: UserStore;
  children: ReactNode;
}>;

export const StoreProvider: StoreComponent = ({
  children,
  store
}): ReactElement => {
  return (
    <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
  );
};

UserStore.ts

export default class UserStore {
  @observable
  public authToken: string = "null";

  @action
  changeAuth = (auth: string) => {
    this.authToken = auth;
  };
}

TestComponent.tsx

function TestComponent() {
  const { changeAuth, authToken } = useContext(StoreContext);

  const handleClick = () => {
    changeAuth("TEST AUTH");
  };

  return (
    <div>
      <button onClick={handleClick}>Click to change Token in store</button>
      <br />
      Token: {authToken}
      <br />
    </div>
  );
}

export default observer(TestComponent);

What I did not notice?


Solution

  • If you were using MobX 6 then you now need to use makeObservable method inside constructor to achieve same functionality with decorators as with MobX 5 before:

    import { makeObservable } from "mobx"
    
    export default class UserStore {
      @observable
      public authToken: string = "null";
    
      @action
      changeAuth = (auth: string) => {
        this.authToken = auth;
      };
    
      constructor() {
        // Just call it here
        makeObservable(this);
      }
    }
    

    Although there is new thing that will probably allow you to drop decorators altogether, makeAutoObservable:

    import { makeAutoObservable } from "mobx"
    
    export default class UserStore {
      // Don't need decorators now
      public authToken: string = "null";
    
      // Don't need decorators now
      changeAuth = (auth: string) => {
        this.authToken = auth;
      };
    
      constructor() {
        // Just call it here
        makeAutoObservable(this);
      }
    }
    

    More info here https://mobx.js.org/migrating-from-4-or-5.html and https://mobx.js.org/react-integration.html