Ext JS to React: Handling Data with Mobx

   JavaScript
Ext JS to React: Handling Data with Mobx

This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.

In a previous article, Handling Application State with Redux, we saw how to use static data to load values into a form and keep the application state in sync as a user edits the form. However, in real world applications, data will often be fetched from a remote server or file. In the following article, we’ll start with the form we built in the previous Redux article and demonstrate how remote can be fetched to populate the form’s fields. We’ll also adjust our previous form to allow form data to be passed in directly making for a more flexible form where data can be furnished or fetched, all with the same form class.

Note: While not a requirement for React, this article’s code examples assume you’re starting with a starter app generated by create-react-app.

React Form Class with MobX

To evolve the form class from the previous article, we don’t need to add all that much logic. Two major factors go into enabling our form to be populated using a remote source. The first aspect that we’ll look at is located in the componentDidMount method. Let’s take a look at the Form class and then discuss what we’ve added:

import React, { Component } from 'react';
import { action, extendObservable, observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import UserStore from './UserStore';

@inject(({ store }, { user }) => {
  return {
    store,
    user
  };
})
@observer
class Form extends Component {
  static defaultProps = {
    store: new UserStore(),
    @observable user: {}
  }

  @action
  componentDidMount() {
    const { store, user, userId } = this.props;

    if (store.fetchUser && userId) {
      store
        .fetchUser(userId)
        .then(userData => Object.assign(user, userData))
        .catch(console.log);
    }
  }

  render() {
    const { user } = this.props;
    const { renderField, submit } = this;

    return (
      <form>
        {renderField(user, 'name')}
        {renderField(user, 'email', undefined, 'email')}
        {renderField(user, 'phone', 'Phone Number', 'tel')}
        {renderField(user, 'company')}
        {renderField(user, 'department')}
        {renderField(user, 'title')}

        <button onClick={submit}>Submit</button>
        <br />
      </form>
    );
  }

  renderField = (user, name, label = name, type = 'text') => {
    if (user[name] == null) {
      extendObservable(user, {
        [name]: ''
      });
    }

    return (
      <div style={{ marginBottom: '12px' }}>
        <label style={{ textTransform: 'capitalize' }}>
          {label}
          <input
            type="text"
            name={name}
            value={user[name]}
            onChange={this.handleFieldChange}
            style={{ marginLeft: '12px' }}
          />
        </label>
      </div>
    );
  }

  @action.bound
  handleFieldChange(e) {
    const { onChange, user } = this.props;
    const { name, value } = e.target;

    user[name] = value;

    if (onChange) {
      onChange({ [name]: value });
    }
  }

  submit = e => {
    e.preventDefault();

    // do submit
  };
}

export default Form;

React Form Class Explained

In the original Form we apply any passed data object in the user prop to the MobX state object which we then use to populate the form. In our now modified Form’s componentDidMount method we now look for a userId prop. If the userId prop is present (we also check to make sure that the state object has a fetchUser method) we will call store.fetchUser(userId). The object that is returned from fetchUser is set on the global state object’s user node.

MobX Global State Class

The global state object we’re passing to the Form is not only used to host the active user state, but also fetch remote user data for the state object. The UserStore is passed in via the store prop on the Form making the fetchUser method available to any component attached to the global state object. Below is our modified UserStore class:

import { action, observable } from 'mobx';

export default class UserStore {
  @observable user = {};

  @action
  fetchUser (id) {
    return fetch('/user.json')
    .then(resp => resp.json())
    .catch(e => console.log(e));
  }
}

The fetchUser method uses the fetch API to asynchronously read a user.json file to return a sample user object to the form. Any issues in reading the file will be caught by the Promise catch method.

Sample User Data

Below is the sample user object returned from the public/user.json file:

{
  "id": 1,
  "name": "Don Draper",
  "email": "don.draper@scdp.com",
  "phone": "+1 (212) 555-0112",
  "company": "Sterling Cooper Draper Pryce",
  "department": "Marketing",
  "title": "Creative Director"
}

Connecting the MobX State Object to the Application

Passing the global state object to our App is the same as with our previous article:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'mobx-react';
import UserStore from './UserStore';

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

We pass the UserStore class instance to the Provider making it and any of its properties and methods available to the views composed within the app.

Root Application Class

Finally, let’s look at the App class that is the entry point for our app and composes in our Form:

import React from 'react';
import Form from './Form';

export default () => <Form userId={1}/>;


Ext JS to React: Handling Data with Mobx



In this example, we pass a userId prop which prompts the fetching of remote user data. In this example, the value passed isn’t important. In a real world app, the value passed would be used to determine the user record returned from the server.

Wrap Up

MobX makes easy work of sharing data and functions across all views in an application. In the example above we were able to call up the needed form data form a remote file using a method on the UserStore available to any view within the app. Then, by setting the user object back on the UserStore instance, any view within the app has access to the current user data, not just the form that fetched the data initially.


This website uses cookies

These cookies are used to collect information about how you interact with our website and allow us to remember you. We use this information in order to improve and customize your browsing experience, and for analytics and metrics about our visitors both on this website and other media. To find out more about the cookies we use, see our Privacy Policy.

Please consent to the use of cookies before continuing to browse our site.

Like What You See?

Got any questions?


>