mobxjs / mobx-react

React bindings for MobX
https://mobx.js.org/react-integration.html
MIT License
4.85k stars 349 forks source link

React component won't rerender when Observables change #767

Closed FredrikSigvartsen closed 4 years ago

FredrikSigvartsen commented 5 years ago

React component won't rerender

Hi everyone.

I'm fully aware of earlier issues regarding mine, but I can't seem to find an answer that matches mine. So that is the reason I publish this here.

I'm working in a Gatsbyjs project (React based), and I'm having trouble with my props not updating, and therefore the component would not rerender.

With Chrome plugin Mobx-devtools, I can see that my actions are triggered, and I also see that the state is changing. But, it won't trigger a an update in the React component. But when I trigger a new render(), the updated state is showing.

What do I have to do, so the React component automatically rerenders?

Here is my code: ShopModel.js:

import { observable, action, decorate } from  'mobx';
class  ShopModel {
   compassDirection  =  -20;
   cagesActive  =  [{ title:  '1' }, { title:  '2' }];
   cagesInactive  =  [{ title:  '103' }];
   hardwarePackageSelected  =  'vision';
   screenSize  =  42;
   numberOfScreens  =  2;
   recaroChair  =  false;
   AddActiveCage() {
       this.cagesActive.push({ title: `${this.cagesActive.length  +  1}` });
   }

    // AND SIMILAR ACTIONS AS AddActiveCage

decorate(ShopModel, {
    compassDirection:  observable,
    cagesActive:  observable,
    cagesInactive:  observable,
    AddActiveCage:  action,
    RemoveLastActiveCage:  action,
    AddInactiveCage:  action,
    RemoveLastInactiveCage:  action,
    DecrementCompassDirection:  action,
    IncrementCompassDirection:  action,
});
const  ShopStore  =  new  ShopModel();
export  default  ShopStore;

Positioning.js:

import  React  from  'react';
import { inject, observer } from  'mobx-react';
import  SEO  from  '../../components/Seo';
import  Counter  from  '../../components/Counter';
import { Cage, Box } from  '../../components/DragAndDrop';
import {StoreLayout,StoreMainContentLeft,StoreMainContentRight,} from  '../../components/Store';

@inject('shopStore')
@observer
class  PositioningSite  extends  React.Component {
    constructor(props) {
        super(props);
        this.state  = {
            activeDrags:  0,
        };
    }
    render() {
        return (
        <StoreLayout  next="/butikk/hardware">

<>

            <SEO  title="Bestill BlueThink"  />
            <StoreMainContentLeft  id="drag-and-drop">
                <Box
                    className="box"
                    title="Fôringsflåte"
                    onStart={this.onStart}
                    onStop={this.onStop}
                />

            {this.props.shopStore.cagesActive.map(cage => { return (

                <Cage
                key={cage.title}
                className="box"
                title={cage.title}
                onStart={this.onStart}
                onStop={this.onStop}
                />
            );})}
</StoreMainContentLeft>
<StoreMainContentRight>
    <h1>Positioning</h1>
    <h3><strong>Box</strong></h3>
    <Counter
        title="Kompassretning"
        counter={this.props.shopStore.compassDirection}
        onIncrement={() =>
        this.props.shopStore.IncrementCompassDirection()
        }
        onDecrement={() =>
        this.props.shopStore.DecrementCompassDirection()
        }
    />
    <h3><strong>Cage</strong></h3>
    <Counter
        title="Active"
        counter={this.props.shopStore.cagesActive.length}
        onIncrement={() =>  this.props.shopStore.AddActiveCage()}
        onDecrement={() =>  this.props.shopStore.RemoveLastActiveCage()}
    />
    <Counter
        title="Inactive"
        counter={this.props.shopStore.cagesInactive.length}
        onIncrement={() =>  this.props.shopStore.AddInactiveCage()}
        onDecrement={() =>  this.props.shopStore.RemoveLastInactiveCage()}
    />
</StoreMainContentRight>
</>
</StoreLayout>

);}}

export  default  PositioningSite;

wrap-with-provider.js:

import React from 'react';
import { Provider } from 'mobx-react';
import { useStrict } from 'mobx';

import ShopModel from './src/models/ShopModel';

useStrict(true);

// eslint-disable-next-line react/display-name,react/prop-types
export default ({ element }) => (
  <Provider shopStore={ShopModel}>{element}</Provider>
);
danielkcz commented 5 years ago

Please provide a reproduction, ideally with CodeSandbox... Or simply wait and hope someone would be willing to dig through that unformatted piece of code ;)

FredrikSigvartsen commented 5 years ago

Hi again,

Thank you for answering. I've made a reproduction here: https://codesandbox.io/s/gatsby-starter-default-ixlg8?fontsize=14

This is code example. But I'm having some Node-issues on Codesandbox that I don't seem to fix. Is that still a problem?

danielkcz commented 5 years ago

Well, you shouldn't be using Gatsby :) There is a one for React.

Sorry, did not realize you have a Gatsby project actually :) I know nothing about that, so hopefully, someone else can chime in.

FredrikSigvartsen commented 5 years ago

I've added it to a public repo: https://github.com/FredrikSigvartsen/glowing-octo-couscous It's also public: https://quirky-chandrasekhar-6d307e.netlify.com/shop/positioning/

The mobx actions don't work in production, only using "gatsby develop". I'm not sure why.

mweststrate commented 4 years ago

Please note that @babel/plugin-proposal-class-properties should be configured with loose: true in your babelrc. Not sure if that is the cause, but it is incorrect at least. See https://mobx.js.org/best/decorators.html#enabling-decorator-syntax.

mweststrate commented 4 years ago

Oh this issue was inactive for a year already. Closing for now.