-1

https://codesandbox.io/s/react-mobx-change-value-in-several-components-f2tuu

How to change a variable from different places? The variable is one, but it changes separately in different components and these changes made in one component are not reflected in the other.

App.js

import AppStore from "./AppStore";
import { observer } from "mobx-react";
import SomeComponent from "./SomeComponent";

const store = AppStore();

const App = observer(() => {
  return (
    <div className="App">
      <div>
        <i>This is text in App:</i> {store.text}
      </div>
      <div>
        <button
          onClick={() => {
            store.changeText("This is text from App");
          }}
        >
          Change text from App
        </button>
      </div>

      <SomeComponent />

    </div>
  );
});

export default App;

SomeComponent.js

import React from "react";
import AppStore from "./AppStore";
import { observer } from "mobx-react";

const store = AppStore();

const SomeComponent = observer((props) => {
  return (
    <div>
      <div>
        <i>This is text in component:</i> {store.text}
      </div>
      <div>
        <button 
            onClick={() => store.changeText("This is text from component")}
        >
          Change text from component
        </button>
      </div>
    </div>
  );
});

export default SomeComponent;

AppStore.js

import { action, makeObservable, observable } from "mobx";

export default function AppStore() {
  return makeObservable(
    {
      text: "Initial text",
      changeText(data) {
        this.text = data + ": " + new Date().toLocaleTimeString();
      }
    },
    {
      text: observable,
      changeText: action
    }
  );
}
ArtSur
  • 9
  • 4

2 Answers2

0

Inside your App component, you created an object variable store via this code const store = AppStore();. Then you use it, display it, change it and so on. Later on inside your SomeComponent component you create a NEW variable store via const store = AppStore();. Even tho the variable names are the same and using the object, these are 2 DIFFERENT variables. Hence why updating one does not update the other. Inside the DOM they are a different object. To fix it instead of creating a new object inside the SomeComponent just pass store from the parent component. Inside the App, when rendering SomeComponent pass it the store variable like so

<SomeComponent store={store} />

And inside your SomeComponent take it from props and use it

import React from "react";
import { observer } from "mobx-react";

const SomeComponent = observer(({ store }) => {
  return (
    <div>
      <div>
        <i>This is text in component:</i> {store.text}
      </div>
      <div>
        <button onClick={() => store.changeText("This is text from component")}>
          Change text from component
        </button>
      </div>
    </div>
  );
});

export default SomeComponent;

This way both components can change the same variables data.

EDIT. I forked your project and edited the code to match what I posted here so you would have a live example to analize. https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-4c7uk?file=/src/SomeComponent.js

Lith
  • 398
  • 3
  • 13
  • Thank you. Thank. It works. But is there really no way to use a single store in different components without props? – ArtSur Mar 24 '21 at 06:34
  • @ArtSur Your welcome. There are wayst, kinda. You can use redux. You will still have to use props, but instead of passing down from parent to childs, that can get annoying and long the more child components you have, you have a globalized store that you may pick whatever data you wish from entire project using containers and modifying any data you wish from anywhere using actions. Now i heared react released thing called memo, if i recall it correctly, that suppost to do same as redux but easier. But i never tried it. With `mobx-react` i do not know. I never used nor heared of it, sorry – Lith Mar 24 '21 at 06:48
  • regardless, you have to have only single instance of a store. cant execute multiple `createStore` methods sicne that would create entire new store. – Lith Mar 24 '21 at 06:50
0

I found sulution with React Context. Sorry for not adding here right away. Unfortunately, it is difficult to understand the Mobx documentation.

https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh

First, we need to make context and create provider:

https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/AppStoreProvider.js

import { useLocalStore } from "mobx-react";
import React from "react";
import AppStore from "./AppStore";

export const AppStoreContext = React.createContext();

export const AppStoreProvider = ({ children }) => {
  const store = useLocalStore(AppStore);
  return (
    <AppStoreContext.Provider value={store}>
      {children}
    </AppStoreContext.Provider>
  );
};

export const UseAppStore = () => React.useContext(AppStoreContext);

export default AppStoreProvider;

Where the AppStore.js is:

import { action, makeObservable, observable } from "mobx";

export default function AppStore() {
  return makeObservable(
    {
      text: "Initial text",
      changeText(data) {
        this.text = data + ": " + new Date().toLocaleTimeString();
      }
    },
    {
      text: observable,
      changeText: action
    }
  );
}

https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/AppStore.js

Then, we need to wrap our main render in index.js with :

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import AppStoreProvider from "./AppStoreProvider";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <AppStoreProvider>
    <App />
  </AppStoreProvider>,
  rootElement
);

https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/index.js

And then we can use context:

import "./styles.css";
import React from "react";
import { UseAppStore } from "./AppStoreProvider";
import { observer } from "mobx-react";
import SomeComponent from "./SomeComponent";

const App = observer(() => {
  const store = UseAppStore();

  return (
    <div className="App">
      <div>
        <i>This is text in App:</i> {store?.text}
      </div>
      <div>
        <button
          onClick={() => {
            store.changeText("This is text from App");
          }}
        >
          Change text from App
        </button>
      </div>

      <SomeComponent />

    </div>
  );
});

export default App;

https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/App.js

Or like this:

import { useContext } from "react";
import { AppStoreContext } from "./AppStoreProvider";
import { observer } from "mobx-react";

const SomeComponent = observer((props) => {
  const store = useContext(AppStoreContext);

  return (
    <div>
      <div>
        <i>This is text in component:</i> {store.text}
      </div>
      <div>
        <button onClick={() => store.changeText("This is text from component")}>
          Change text from component
        </button>
      </div>
    </div>
  );
});

export default SomeComponent;

https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/SomeComponent.js

Hope it comes in handy for someone

ArtSur
  • 9
  • 4