Search code examples
reactjsreact-hooksmobxmobx-reactmobx-react-lite

Mobx with useEffect() not rendered data in component


I'm new to React and Mobx.

I want to init component by data from GET-request in useEffect(). There are simple component with mobx store:

import { autorun } from "mobx";
import React, { useEffect, useRef } from "react";
import { useStore } from "./store";

const TestPage: React.FC = () => {

    const { testStore } = useStore();
    const isCancelled = useRef(false);

    useEffect(
        () => autorun(
            () => {
            
            const fetchData = async () => {
                const data = await fetch('/api/dictionaries/sources?subject=MATH');
                const json = await data.json();

                if (!isCancelled.current) {
                    testStore.setList(json);
                }
            }

            fetchData().catch(console.error);
            return () => {
                isCancelled.current = true;
            };

        }
        )
        , [testStore, testStore.setList]
    );

    return (
        <div>
            <h1>This is test list</h1>
            {testStore.list.map((i) => <div key={i.id}>{i.id}: {i.name}</div>)}
        </div>
    );
}

export default TestPage;

test-store.ts:

import { makeAutoObservable } from "mobx";

export interface Item {
    id: string;
    name: string;
}

export default class TestStore {

    list: Item[];
 
    constructor() {
        this.list = [];
        makeAutoObservable(this);
    }

    setList = (list: any) => {
        this.list = list; 
        console.log("In setList: " + this.list);
    }

}

store.tx:

import { createContext, useContext } from "react";
import TestStore from "./test-store";

const store = {
    testStore: new TestStore(),
};

export const StoreContext = createContext(store);

export const useStore = () => {
  return useContext<typeof store>(StoreContext);
};

export default store;

Response from server is received, but list in component is not rendered: enter image description here

However, if recompile to a hot one, the data in the component is rendered. So I think the problem is asynchronous execution. Can I fix it?


Solution

  • For making the React Components reactive to the state changes in mobX, we need to wrap the components an observer(), which is a HoC. You can use either mobx-react or mobx-react-lite both works fine.

    import { observer } from "mobx-react-lite"
    const Component = observer(() => {
       return (
          // JSX Codes that uses mobX states
       )
    })
    
    export default Component
    

    Otherwise, you can also do it like this

    import { observer } from "mobx-react-lite"
        const Component = () => {
           return (
              // JSX Codes that uses mobX states
           )
        }
        
        export default observer(Component)
    

    Only then, you can trigger rerender. Othewise, if you have to use the context API, then I think you have to use useState() to trigger the UI render.