React.JS – Best and Most Underrated Pattern Design

<Modal showCloseButton>
	<title>Modal title</title>
	<contents>Modal body text goes here.</contents>
	<dismissButton onClick={close}>Close</dismissButton>
	<actionButton> onClick={save}>Save</actionButton>
</Modal> 

The times are near when we would like to pass props and would want to control the behavior of child elements. For example, in the picture given below, you can see there are different elements:

  • A title section that is written at the top.
  • A cross button to close the action present at the top right corner.
  • A content area where you can write some text.
  • A button to save the changes, “Save changes”.
  • A dismiss button to close it, “Close”.

Now, if we want to reuse the Modal properly, we must modify the modal and its elements. This means that users will get control over things such as the content which is displayed, the dispatched events, the style of the content, and all other elements of the Modal that will be under the control of the user. Users can select a naïve solution for the acceptance of proposed for each element like they are:

<Modal
  showCloseButton
  showDismissButton
  showActionButton
  title="Modal title"
  contents="Modal body text goes here."
  dismissButtonText="Close"
  actionButtonText="Save changes"
  handleDismiss={close}
  handleAction={save}
/>

The problem with this kind of approach is that it spams the mechanism of propos, and this makes the whole component look less readable and more inflated. While on the one hand, it is less attractive, at the other end, it limits the amount of propos that users can pass to child elements. This method also prevents users from having complete control over the elements. However, you can solve this problem through another way by providing generic props objects where every prop object is representing a different element:

<Modal
  showCloseButton
  title="Modal title"
  contents="Modal body text goes here."
  dismissButtonProps={{
    text: 'Close',
    handler: close,
  }}
  actionButtonProps={{
    text: 'Save changes',
    handler: save,
  }}
/>

However, this solution works fine, but it does not solve the issue of spam, and you are completely abusing the syntactic sugar that JSX provides you in this way. You are obligated to use JSONs style attributes instead of using the HTML style attribute assignments (attr= “value”).

Using Bootstrap for the rescue

They take a very shrewd approach in Bootstrap, and that is instead of defining props all over the places, they gave us the capability to manipulate the Modal’s children directly. Now, we can achieve this intended functionality by using the dedicated components that Bootstrap was aiming for:

<Modal.Dialog>
  <Modal. Header closeButton>
    <Modal.Title>Modal title</Modal.Title>
  </Modal.Header>

  <Modal.Body>
    <p>Modal body text goes here.</p>
  </Modal.Body>

  <Modal.Footer>
    <Button variant="secondary" onClick={close}>
      Close
    </Button>
    <Button variant="primary" onClick={save}>
      Save changes
    </Button>
  </Modal.Footer>
</Modal.Dialog>

As you can see, the progress is there, and things are getting better, but we can take it to the next level. 

Although things are very declarative and clear and we can stay here, but we are still obligated to compose an entire modal to the next level. But this would mean that we would not be able to use the Modal’s children to cover up the missing pieces, as we implemented the part of the logic before. Normally, it is not like that we have to write the Modal’s content from scratch.

Moreover, you can use it as a template for your use in future cases. However, there is no filter or restriction on the children’s input, and you can use it the way you want. But normally, we would like it if the user uses only a few limited elements so that he does not mess up the things, and if that is the case, we must see what could be the right approach to follow it.

Introducing the design pattern that contains everything

Now, if we see at our last progress and calculate what we gathered so far, the new pattern should have the following attributes:

  • Has complete control over child elements using props.children.
  • Has a template already in place for the user.
  • No spamming of the mechanism of props.
  • Has restrictions on the input.

Well, this sounds good and promising so let’s take a look at an example and we will be using Bootstrap Modal component for it as an anchor:

const ModalFromTheFuture = ({ showCloseButton, children }) => {
  const childProps = useChildProps(props.children, [
    'title',
    'contents'
    'dismissButton',
    'actionButton',
  ]);

  return (
    <Modal.Dialog>
      <Modal.Header closeButton={showCloseButton}>
        {childProps.title &amp;&amp; <Modal.Title {...childProps.title} />}
      </Modal.Header>

      <Modal.Body>
        {childProps.contents &amp;&amp; <p {...childProps.contents} />}
      </Modal.Body>

      <Modal.Footer>
        {childProps.actionButton &amp;&amp; <Button {...childProps.actionButton} variant="secondary" />}
        {childProps.dismissButton &amp;&amp; <Button {...childProps.dismissButton} variant="primary" />}
      </Modal.Footer>
    </Modal.Dialog>
  );
};

Now you can clearly view that the new modal component uses a hook known as “useChildProps(). Moreover, this hook will basically flatten nested propos by going through the ‘props.children’.  Furthermore, when it will come to make sure that right names are addressed, this method will validate them against a provided white list.

const useChildProps = (children, whitelist) => {
  return useMemo(() =>
    [].concat(children).reduce(
      (childProps, child) => {
        if (whitelist &amp;&amp; !whitelist.includes(child.type)) {
          throw Error(`element <${child.type}> is not supported`);
        }

        childProps[child.type] = child.props;

        return childProps;
      },
      [children]
    )
  );
};
<ModalFromTheFuture showCloseButton>
  <title>Modal title</title>
  <contents>Modal body text goes here.</contents>
  <dismissButton onClick={close}>Close</dismissButton>
  <actionButton onClick={save}>Save changes</actionButton>
</ModalFromTheFuture>

However, we know it will cause confusion with native HTML tag names, but this can be said about any other React component being used by other users or us. But if you see the changing trends, such as the introduction of component-based user-interface, for example, Angular, Vue, React, or other web components, so new tag names are not rare now. Therefore, you should go for the new design and must not stay afraid of getting hands on it.

Leave a comment

Your email address will not be published. Required fields are marked *