Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] mobx-react 9.x has some another BC? Help please. #3730

Closed
MirKml opened this issue Jul 22, 2023 · 7 comments · Fixed by #3731 or #3736 · May be fixed by evelant/mobx#1
Closed

[Bug] mobx-react 9.x has some another BC? Help please. #3730

MirKml opened this issue Jul 22, 2023 · 7 comments · Fixed by #3731 or #3736 · May be fixed by evelant/mobx#1
Labels

Comments

@MirKml
Copy link

MirKml commented Jul 22, 2023

Hello, I try to upgrade from mobx-react 7.6 to latest 9.x but hit the problem. I read your changelog, but I think this one is different then props, state observation.

Our app is huge with many stores and components, I'm still trying to find the root of problem and then make a minimal example but it's complicated to find the root of problem. Please help me. What I found yet

setup before upgrade: react 17.0.2, mobx 6.10.0 mobx-react 7.6.0
after upgrade: react 17.0.2, mobx 6.10.0 mobx-react 9.0.0.

This is component/store simple setup - tab component which observers one store changes and Grid child component with another store. This grid call store for tab component in componentDidMount. Injection store isn't important, it works fine.

@observer
class Tab extends Component {
    store: StoreTab

    render() {
        console.log("tab render");
        return (
            <>
            <div>{this.store.loading.toString()}</div>
            <div>{this.store.mapItems.length}</div>
            <button onClick={() => this.store.setMapItems([0,1,2])}>Test</button>
            <Grid /> // important, see later
            </>
      )}
}

StoreTab {
    mapItems = [];
    loading = false;

    constructor() {
        makeAutoObservable(this);
    }

    // async action, modify/clean mapItems
    async setMapItems(ids) {
        await something();
        runInAction(() => this.mapItems= ....);
    }
}

@observer
class Grid extends Component {
   store: StoreGrid
    
   componentDidMount() {
     this.store.clean(); // action
   }
   
}

StoreGrid {
     storeTab
    
    constructor() {
        makeAutoObservable(this);
    }

    clean() {
       // clears the storeTab mapItems,  it calls without await
       console.log("clearing map items")
       storeTab.setMapItems([]);
    }
}

When app starts with mobx-react 7.6 it writes to the console

tab render
clearing map items
tab render

yes it render parent component twice, then after each button hit it re-renders tab and works fine

After upgrade to mobx-react@9 it renders only first time and nothing more, no reaction for button hit

tab render
clearing map items

I discovered that problem is with Grid component and its componentDidMount, which changes array in parent store. When I comment it out, it works with mobx-react@9 - when i hit button, tab re-rerender.

Can you help me to find what was changes in latest mobx-react, that it breaks such common component, stores usage? Is there another breaking change in mobx-react 9?

@mweststrate
Copy link
Member

mweststrate commented Jul 22, 2023 via email

@MirKml
Copy link
Author

MirKml commented Jul 22, 2023

I will try to find what really stores in componentDidMount do next week, keep issue open please.

But even if it something like you mentioned, can you explain me a little how it is possible that it works fine with [email protected] and doesn't with mobx-react@9? I thought that 9.x changes are related to latest React strict mode, breaks observables for props, state, context. Does 9.x something new with render loop detection, loop breaking?

I ask for better searching cause for potentially another places, situations, when our another mobx applications stop working after upgrade.

Clearing in a didmount sounds potentially dangerous, might want to check if that surely happens only once, or do it async? That sounds like something that could potentially cause render loop?

@mweststrate
Copy link
Member

mweststrate commented Jul 22, 2023 via email

@MirKml
Copy link
Author

MirKml commented Jul 22, 2023

Hm, I discovered what componentDidMount of child does.
It clears the observable array for parent (tab) component store. I changed the first comment, look at it please. There is now better explanation and console.log outputs that explains the behavior before and after upgrade.
Before - it renders parent twice at start and then react for each button hit. After upgrade renders only once and doesn't react - it's broken.

I think that something new breaks the old behaviour. I can prepare some real example next week, but I think it's clear now. What do you mean it's bug?

@urugator
Copy link
Collaborator

urugator commented Jul 22, 2023

We don't react to changes unil the component is mounted. This is to prevent warnings about calling forceUpdate on components that were abandoned (IIRC). So the problem is that parent's componentDidMount is called after child's componentDidMount, so the changes are ignored. I think you can consider it a bug. I am writing a test to verify this atm.

@urugator
Copy link
Collaborator

Fix ready #3731

@MirKml
Copy link
Author

MirKml commented Jul 23, 2023

Thank you guys for such a quick bug review and responses during a weekend.

mweststrate pushed a commit that referenced this issue Jul 24, 2023
…before mount (#3731)

* fix

* typo

* clear flag on unmount, fix typo
This was referenced Jul 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants