5

My Store.js:

import { observable, useStrict, action } from 'mobx';

useStrict(true);

export const state = observable({

    contacts: []

});

export const actions = {

    addContact: action((firstName, lastName) => {
        state.contacts.push({
            id: Math.floor((Math.random() * 1000) + 1),
            firstName: firstName,
            lastName: lastName
        });
    })

};

My index.js:

import React from 'react';
import { Provider } from 'mobx-react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { state, actions } from './Store';

ReactDOM.render(
    <Provider state={state} actions={actions}>
        <App />
    </Provider>,
    document.getElementById('root')
);
registerServiceWorker();

My App.js:

import React, { Component } from 'react';
import ContactList from './components/ContactList';
import ContactAdder from './components/ContactAdder';
import { observer, inject } from 'mobx-react';

const App = inject('state', 'actions')(observer(class App extends Component {
  render() {
    return (
      <div>

        <h1>Contacts</h1>

        <ContactList
          contacts={this.props.state.contacts}
          onDelete={this.props.actions.removeContact}
        />

        <ContactAdder onAdd={this.props.actions.addContact} />

      </div>
    );
  }
}));

export default App;

And finally my ContactAdder.js:

import React, { Component } from 'react';

class ContactAdder extends Component {

    render() {
        return (
            <div className='ContactAdder'>
                <input type='text' value='' placeholder='First Name' />
                <input type='text' value='' placeholder='Last Name' />
                <button onClick={this.props.onAdd.bind(this, 'Test', 'Testerson')}>Add</button>
            </div>
        );
    }

}

export default ContactAdder;

After addContact() runs in Store.js I can see, when I do a console log, that the array does get mutated. But for some reason my view isn't reacting. What am I doing wrong?

  • I don't see anywhere you're actually updating the state of a component that renders. – Cruiser Aug 23 '17 at 12:59
  • @Cruiser I was under the impression that MobX does that for you. If you have an "observable" value that changes, any class that is an "observer" would re-render. Or am I completely wrong? –  Aug 23 '17 at 13:40
  • I think the MobX docs might help: https://mobx.js.org/best/react.html I think you're changing values outside of a tracked function, which doesn't work. Look at the part of the page with this heading: Incorrect: changing a non-observable reference – Cruiser Aug 23 '17 at 13:52
  • I've not used MobX in prod so just trying to sort out possibilities with you – Cruiser Aug 23 '17 at 13:53
  • 1
    What does your `ContactList` look like? Without seeing the component, I would guess that it will work if you make that one into an `observer` as well – Tholle Aug 23 '17 at 15:49
  • 1
    @Tholle that was the issue! You can set this as the answer and I'll accept if you want... So I was under the impression that since the contacts are getting sent to ContactList as props that ContactList would automatically re-render. Setting ContactList to also be an observer solved the issue. –  Aug 24 '17 at 08:57

2 Answers2

4

Without seeing your ContactList component, I think the issue would be resolved if you just make that into an observer as well.

An observer component will re-render once the observables that got de-referenced in the previous render are changed. The this.props.state.contacts does not de-reference your contacts array. It would work if you wrote this.props.state.contacts.slice() or this.props.state.contacts.peek(), but that is generally only used when passing observables to external libraries.

You might just as well make all your components that use observables into observers to be as efficient as possible.

Tholle
  • 108,070
  • 19
  • 198
  • 189
3
<ContactList
  contacts={this.props.state.contacts.toJS()}
  onDelete={this.props.actions.removeContact}
/>

Try to use the toJS() method for state.contacts to convert ObservableArray to Array, then it will update the ContactList.

Donick
  • 31
  • 2