How about use Mediator in React Form?
Tags: react, mediator
Published: 2022-04-25 02:56:02

---
title: How about use Mediator in React Form?
published: true
description: 
tags: React,Mediator
cover_image: https://res.cloudinary.com/practicaldev/image/fetch/s--RlqwldxD--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehd9wcdwrvrn9bu5g4z5.jpg
---

## **EDITED**

Well, maybe using Mediator is not a good idea :)

We can use event (or event-like) to send message from children to parent component.

Put the state in the parent component is not a bad idea. 

See [this comment](https://dev.to/paratron/comment/1npeh) please!

And thank you @lukeshiru, you are the best one!!!

---

## Original Post

Hello everyone. When we try to code in React, we usually write some dialog component with some input component, some button component, and something else.

It is always hard to re-use those input component. Because its behavior is rely on other components.

![Normal way to handle form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aj22str72g7t9wsq53sa.png)

As you see, in the normal way, the input component need to know how does the dialog work (to set the state of the dialog). And the submit button also need to know how to get the state and send a request to server. And the submit button also need to know how to show the error message (in here, the button need to set the state `errorMsg` to `"error"`.

We **can** pass the props in function type. To let those component can be called the 'callback' function. for example:

* Pass the input component a function prop to deal with the event `onChange`. Now when we change the input component, the state of **dialog** will be changed.
* Pass the submit button component a function prop to set the `errorMsg`.

Now those sub-component does not rely on the dialog component. Those component now just rely on those callback function. It is really good. But can it be better?

* If the dialog component has many input components, it will hold many states. Can we just let sub-components hold the states? And can we let the sub-components have a method to get its value?
* In normal way, only the parent component can deal with the logic. What will happen if the parent need to deal with a lot of events? Can the sub-component have methods to be called?

We do not want to let the input components rely on the dialog component, because we want to re-use those in the future. So the best way is let the sub-components rely on the interface of the parent component. In here, the sub-component rely on a function `notify` to call parent component. And the parent's job is just to coordinate sub-components.

For example, now I have a parent component. Its job is to send a request to server with an email address:

```javascript
const Dialog = () => {
  const inputRef = useRef(null);
  const submitRef = useRef(null);

  const notify = (eventName, data) => {
    console.log(eventName, data);

    if (eventName === 'submit') {
      const email = inputRef.current.getContent();
      submitRef.current.submit(email);
    } else if (eventName === 'error') {
      inputRef.current.setErrorMsg(data);
    }
  }

  return (
    <div className={styles.dialog}>
      <Input ref={inputRef} />
      <Submit notify={notify} ref={submitRef} />
    </div>
  )
}
```

As you see, the dialog component rely on two components: `Input` and `Submit`. But `Input` and `Submit` do not rely on `Dialog`. They rely on an interface with a method `notify`.

It is good. It means we can use those components `Input` or `Submit` in another component, which has a method `notify`.

Then we need to define `Input`:

```javascript
const Input = forwardRef((_prop, ref) => {
  const [content, setContent] = useState('');
  const [errorMsg, setErrorMsg] = useState(null);

  useImperativeHandle(ref, () => ({
    getContent: () => {
      return content;
    },
    setErrorMsg: (newErrorMsg) => {
      setErrorMsg(newErrorMsg);
    }
  }), [content]);

  return (
    <>
      <input
        ref={ref}
        className={styles.input}
        onChange={(e) => setContent(e.target.value)}
      />
      { errorMsg ? <span>{errorMsg}</span> : null }
    </>
  )
})
```

It has two method: `getContent` and `setErrorMsg`. So the parent do not need to handle what is the `content` of the component. Neither know the `errorMsg`. It is just hold by the `Input` component.

And here is another component `Submit`:

```javascript
const Submit = forwardRef(({ notify }, ref) => {
  useImperativeHandle(ref, () => ({
    submit: (email) => {
      console.log('Try to fetch email:', email);
      console.error(`Sorry it (${email}) is not the vaild email`);
      notify('error', `Sorry it (${email}) is not the vaild email`);
    }
  }));

  return (
    <button
      ref={ref}
      className={styles.submit}
      onClick={() => notify('submit')}
    >
      Submit
    </button>
  )
});
```

Cool right? The `Submit` component will ask `Dialog` to submit. And the `Dialog` will call its method to submit to a server. It is helpful if you want to put the submit button into another component.