POST with Mobx in functional React component
Tags: mobx, react
Published: 2020-12-21 03:20:37

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](https://dev.to/ndrean/a-comparison-of-state-management-in-react-with-mobx-vs-state-lifting-54l4) which showed how to use Mobx in functional React form stateless component.

## Code linting

We use the helper `<React.StrictMode>` and the [following config](https://mobx.js.org/configuration.html#linting-options) for Mobx to detect errors.

```js
#index.js
import { configure } from "mobx";
configure({
  enforceActions: "always",
  computedRequiresReaction: true,
  reactionRequiresObservable: true,
  observableRequiresReaction: true,
  disableErrorBoundaries: true,
});
```

## Guideline
We follow the [Mobx async guides](https://mobx.js.org/actions.html#updating-state-using-actions).

## 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:

```js
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](https://mobx.js.org/actions.html#wrapping-functions-using-action) with `action`
- wrap the [asynchronous call](https://mobx.js.org/actions.html#runinaction) with `runInAction(()=> [...])` or use the IIEE form `action(()=>[...])()` 
- wrap a component with `observer` whenever you access to observable values,
- wrap the store [with `observable`](https://mobx.js.org/observable-state.html#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. 

```js
#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:
```js
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).


```js
#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.

```js
  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:
```js
#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.