This is the second post on the discovery of Mobx. We make a stateless functional component that makes asynchronous POST requests, with no classes nor decorators. It is a continuation of the previous post which showed how to use Mobx in functional React form stateless component.
Code linting
We use the helper <React.StrictMode>
and the following config for Mobx to detect errors.
#index.js
import { configure } from "mobx";
configure({
enforceActions: "always",
computedRequiresReaction: true,
reactionRequiresObservable: true,
observableRequiresReaction: true,
disableErrorBoundaries: true,
});
Guideline
We follow the Mobx async guides.
The component
We will build a very simple React component that displays an input and POST it to some API. Then we display the response from the server. The fetched data will be stored in the object myStore
Imports from Mobx
Our imports are:
import React, { useState } from "react";
import { observable, action, runInAction } from "mobx";
import { observer } from "mobx-react-lite"
These imports are used with the following rules:
- wrap the event handlers with
action
- wrap the asynchronous call with
runInAction(()=> [...])
or use the IIEE formaction(()=>[...])()
- wrap a component with
observer
whenever you access to observable values, - wrap the store with
observable
The Store
The state is an object, named myStore
here, and contains the observables
, the values and methods that may be changed by the component: the input - uncontrolled value here - name
and the returned result
from the server and a rendering method.
A component may still have a local state; for example, the loading state is naturally local to each component.
#myStore.js
import React from "react";
import { observable } from "mobx";
const myStore = observable({
name:"",
result: [],
getResult(bool) {
return bool ? Loading() : myStore.result;
},
});
with the helper:
const Loading = () => <span>Loading ...</span>;
The code
The whole component is wrapped by observer
since we access to observable values.
The onSubmit handler is wrapped with action
. The component doesn't use an internal state for the data nor use useEffect
. Instead, the state mutation will be handled by runInAction
, an "autorun" form of action
(the IIEF form of action
can also be used).
#FormPstMobx.js
export const FormPostMobx = observer(({store}) => {
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = action(async (e) => {
e.preventDefault();
setIsLoading(true);
const formData = new FormData(e.currentTarget);
// Object.fromEntries(formData) to check
try {
const query = await fetch("url", {
method: "POST",
mode: "cors",
body: formData,
});
if (query) {
const res = await query.json();
runInAction(()=> store.result = res)
// or action(() => { store.result = res })()
}
} catch (error) {
console.error("Error ", error);
throw error;
} finally {
setIsLoading(false);
}
});
We return a standard form with uncontrolled input. The last line displays conditionally whether the Loader or the results. This method comes from the store as per the Mobx linting.
return (
<>
<form onSubmit={handleSubmit} >
<input type="text" name="data[name]" defaultValue={store.name} />
<button>Enter</button>
</form>
<p>{store.getResult(isLoading)}</p>
</>
);
});
and we can use:
#index.js
import ReactDom from "react-dom";
import { myStore } from "./store";
import { FormPostMobx} from "./FormPostMobx.js
ReactDom.render(
<React.StrictMode>
<FormPostMobx store={myStore}/>
</React.StrictMode>,
document.getElementById('root')
)
Conclusion
With very little change in the code, we can use Mobx with asynchronous calls. This makes the component stateless. We then can continue and enjoy a simple way to use a central store so the data can be distilled easily within the rest of the components.
Top comments (0)