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}/>;
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.
Seth Lemmons
Related Posts
-
Ext JS to React: FAQ
This is part of the Ext JS to React blog series. React is Facebook's breakout…
-
Ext JS to React: Migration to Open Source
Worried about Migrating from Ext JS? Modus has the Answers Idera’s acquisition of Sencha has…