Skip to content

Mobx state updating in console log, but not re-rendering

I’ve created a MobX store (React Native project) and am updating a state value on button click. The state is successfully updating as displayed in a console log, but the DOM is not re-rendering with the updated state.

Most answers on SO are a little outdated as they recommend adding @observable in the right places, but the latest MobX docs say to use a different syntax with makeAutoObservable(this, { key: value }).

(EDIT: Codesandbox so you can see in Home.js the console logging on button press, but “You love…” not updating with mobx store value)

Here’s my current set up:

store.js

import { makeAutoObservable, observable } from "mobx";

export class ChooseTea {
  tea;

  constructor() {
    makeAutoObservable(this, {
      tea: observable,
    });
  }

  selectTea = (tea) => {
    this.tea = tea;
  };
}

Home.js

import { ChooseTea } from "../data/store";
import { observer } from "mobx-react";

export const Home = observer(() => {
  const store = new ChooseTea();

  const handleChildChoose = (tea) => {
    store.selectTea(tea); // value passed to store
    console.log(store.tea); // successfully logs the new chosen tea
  };

  return (
    <View style={styles.container}>
      <Text>You love {store.tea}</Text> // does not update on new tea chosen
      <View style={styles.teaCardContainer}>
        {teaData &&
          teaData.map((teaObj) => (
            <TeaCard
              id={teaObj.id}
              teaData={teaObj}
              key={teaObj.id}
              strength={teaStrength * 2}
              handleChoose={handleChildChoose}
            />
          ))}
      </View>
    </View>
  );
});

TeaCard.js

function TeaCard({ teaData, handleChoose }) {
  const handleClick = (tea) => {
    handleChoose(tea); // value passed to parent component (Home.js)
  };

  return (
    <View>
      <View>
        <Text>{teaData.name}</Text>
      </View>
      <Rating count={teaData.rating} />
      <Button
        title="Choose"
        onPress={() => handleClick(teaData.name)}
        color={AppStyles.colour.secondary}
       />
    </View>
  )
}

Answer

This line is the problem:

const store = new ChooseTea();

You are recreating you store on every render. Your component actually react to changes, starts rerendering, but then new store is created and tea is not selected there by default. So you are changing data in your old store, but then use data from newly created one.

What you can do is use useState or useMemo for your store, like that:

const [store] = useState(() => new ChooseTea());

You also need to define all your properties, it won’t work otherwise (or at least without extra configuration):

export class ChooseTea {
  // Use null or empty string "", for example
  tea = null;

  constructor() {
    makeAutoObservable(this);
  }

  selectTea = (tea) => {
    this.tea = tea;
  };
}

Codesandbox