reactjstypescriptmobxmobx-reactmobx-state-tree

How use Mobx 6 storage with React 17?


I'm completely confused with the rules of mobX - react . Some methods in another project work, but not in this test.

Below is the code of my components from the test application

App.js

import React, { FC } from 'react';
import "./App.css";
import {observer} from 'mobx-react';
import ComponentFirst from './components/ComponentFirst/ComponentFirst';
import ComponentSecond from './components/ComponentSecond/ComponentSecond';


const App: FC = observer(() => {
  return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <ComponentFirst />
        <ComponentSecond />
      </div>
  );
})

export default App;

ComponentFirst

import React, { FC } from 'react';
import {testStoreFirst} from '../../stores/testStoreFirst';

const ComponentFirst : FC = () => {
    return (
        <div>
            <h3>It is First Component</h3>
            <p>{testStoreFirst.testText}</p>
            <p>{testStoreFirst.textForSecondTestStore}</p>
            <button onClick={() => {testStoreFirst.setTestText('New text after click')}}>Click me!!!</button>
        </div>
    )
}

export default ComponentFirst;

ComponentSecond

import React, { FC } from 'react';
import {testStoreSecond} from '../../stores/testStoreSecond';
import {testStoreFirst} from '../../stores/testStoreFirst';

const ComponentSecond : FC = () => {
    return (
        <div>
            <h3>It is Second Component</h3>
            <p>{testStoreSecond.textFromFirstStore}</p>
            <button onClick={() =>{testStoreFirst.setTextForSecondTestStore('I can change text from second storage')}}>Click me!!!</button>
        </div>
    )
}

export default ComponentSecond;

testStoreFirst

import { makeAutoObservable} from "mobx";

class TestStoreFirst  {
    testText='It is test text from mobX storage';
    textForSecondTestStore='this text from First Store!!!';

    constructor() {
        makeAutoObservable(this);
    }


    setTextForSecondTestStore = (newText : string) => {
        this.textForSecondTestStore = newText;
    }

    setTestText = (newText: string) => {
        this.testText = newText;
        console.log('It is not work');
    }

}

export const testStoreFirst = new TestStoreFirst()

testStoreSecond

import {makeAutoObservable} from 'mobx'
import {testStoreFirst} from './testStoreFirst'

class TestStoreSecond {
    textFromFirstStore = testStoreFirst.textForSecondTestStore

    constructor() {
        makeAutoObservable(this);
    }
}

export const testStoreSecond = new TestStoreSecond();

My question

My App component is subscribed via observe to changes in stores. By clicking on the first button, in 1 component the text in the storage and, accordingly, the text should change, but it does not change. And In the second component, the value for the text field is taken from testStoreSecond. There, the text field is taken from testStoreFirst . When the button is clicked, the method from testStoreFirst is executed which should change the text, but it does not change.

I've read the documentation, but I still don't fully understand how to use the store and achieve "reactivity" and instant change of the component.


Solution

  • You need to wrap every component that uses any observable values with observer decorator, like you did with an App. But in case of App it's actually useless because you are not using observable values there. So just wrap other components and it should work fine.

    As for this line textFromFirstStore = testStoreFirst.textForSecondTestStore it won't work like you expected because you just assigned value of testStoreFirst.textForSecondTestStore to textFromFirstStore and that's it.

    To make such value reactive you need to use computed value. To make computed you just need to setup a getter function, like that:

    class TestStoreSecond {
      // ...
    
      get textFromFirstStore() {
        return testStoreFirst.textForSecondTestStore
      }
    
      // ...
    }
    
    // And in React access it just as before (it's a getter, not a function)
    <p>{testStoreSecond.textFromFirstStore}</p>