DEV Community

Cover image for Bulding a Twitter What's Happening Bar Form in Reactjs
Antonio Pangallo
Antonio Pangallo

Posted on • Edited on

Bulding a Twitter What's Happening Bar Form in Reactjs

In today post we will use usetheform + draftjs (Rich Text Editor Framework for React) to build an example of a Twitter what's happening form bar.

🎉 Final Result 🎉

Introduction

When we post a new Tweet it basically consists of a collection of information like images, videos, plain text, emoji, ...etc which will be sent to Twitter through a POST api.

If we wanted to build something similar to its what's happening bar, we could think to develop it as a web Form.

A web form consists of a collection of HTML field elements grouped within a <form> tag. HTML supports different field elements, like for instance:

  • <input /> which defines an HTML form for user input
  • <textarea /> which defines a multi-line input control (text area)
  • <select /> which defines a drop-down list

For the complete list of all the HTML form elements supported, please refer to w3schools form elements.

Setting up the Form Skeleton

In React, we can declaratively reproduce our form skeleton like the following:

import React from "react";
import { Form } from "usetheform";
// ...rest of the imports 

const MAX_CHARS_ALLOWED = 50;
export default function App() {
  return (
   <Form onSubmit={(formState) => console.log(formState) }>
     <WhatsHappeningBar maxChars={MAX_CHARS_ALLOWED} />
     <Preview />
     <PrivacyPicker />
     <UploadMediaBar />
     <CharacterCounter maxChars={MAX_CHARS_ALLOWED} />
     <Submit /> 
   </Form>
  );
 }
Enter fullscreen mode Exit fullscreen mode

When an user submits the form, the value of formState will look like:

const formState= {
   editor: { 
     editorState: {}, // the Draftjs editor state
     refEditor: {}, // a DOM ref to the Draftjs editor
     plainText: "abc ...etc"
   },
   postPrivacy: "0", // possible values "0","1","2"
   gif: { }, // present if any gif is uploaded,
   media: [img, video, ..etc] // present if any media is uploaded
}
Enter fullscreen mode Exit fullscreen mode

To better understand how the above formState is composed we will focus on the components that create and handle the main "pieces" of the formState.

Let's start

The first important "piece" of the formState we are going to analyze is the editor:

const formState= {
   ....,
   editor: { 
     editorState: {}, // the Draftjs editor state
     refEditor: {}, // a DOM ref to the Draftjs editor
     plainText: "abc ...etc"
   }
}
Enter fullscreen mode Exit fullscreen mode

which is created and handled by the <WhatsHappeningBar /> component.

⚛️ WhatsHappeningBar

import React from "react";
import { Collection, Input } from "usetheform";
import { DraftEditor } from "./DraftEditor";
import { extractPlainText } from "./utils/extractPlainText";
import { limitTo } from "./utils/limitTo";

export const WhatsHappeningBar = ({ maxChars }) => {
  return (
    <Collection
      object
      name="editor"
      validators={[limitTo(maxChars)]}
      reducers={extractPlainText}
    >
      <DraftEditor name="editorState" maxChars={maxChars} />
      <Input type="hidden" name="plainText" />
    </Collection>
  );
};
Enter fullscreen mode Exit fullscreen mode

An object or an array of "usetheform" library is represented by the <Collection /> component, which creates within the form state the editor object.

Collection component contains the validation function which validates the form state based on the text length of the editor and also a reducer function which extracts the "plainText" from the draftjs "editorState". The full code at: validator and reducer

For more details on how <Collection /> works, please refer to the Collection docs.

The second "piece" within the formState we will look at is the postPrivacy

const formState= {
   ....,
   postPrivacy: "0", // possible values "0","1","2"
}
Enter fullscreen mode Exit fullscreen mode

which is created and handled by the <PrivacyPicker /> component.

⚛️ PrivacyPicker

For sake of simplicity, what is shown below is a basic implementation of the <PrivacyPicker /> component made up be three input radios. Full implementation at: PrivacyPicker component

import React from "react";
import { Input} from "usetheform";

export const PrivacyPicker = () => {
  return (
   <div className="PrivacySelection__Radios">
     <Input name="postPrivacy" type="radio" value="0" checked />
     <Input name="postPrivacy" type="radio" value="1" />
     <Input name="postPrivacy" type="radio" value="2" />
   </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

The <Input type="radio" /> component of "usetheform" creates a piece of state within a Form named "postPrivacy", which holds the privacy value picked by the user. More details about it at: Input docs.

Another component worthy to be mentioned is the <UploadGif />
which creates and handles the following "piece" of formState:

const formState= {
   ....,
   gif: { ...gifProps }
}
Enter fullscreen mode Exit fullscreen mode

⚛️ UploadGif

import React, { useEffect, useState } from "react";
import { useField } from "usetheform";

export const UploadGif = () => {
  const { setValue } = useField({ type: "custom", name: "gif" });
  const [showGrid, toggleGrid] = useState(() => false);
  const toggleGifGrid = () => toggleGrid((prev) => !prev);
  const onGifClick = (gif, e) => {
    e.preventDefault();
    setValue(gif);
    toggleGifGrid();
  };
  return (
    <div>
      <button type="button" onClick={toggleGifGrid}>
        <img alt="Upload GIF" src={UpladGifSVG} />
      </button>
      {showGrid && (
        <GifGrid onCloseGifGrid={toggleGifGrid} onGifClick={onGifClick} />
      )}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

The useField hook allows to build a custom input primitives.
When a user picks any gif image a callback function will be invoked and the gif object will be pushed into the formState:

  const onGifClick = (gif, e) => {
    e.preventDefault();
    setValue(gif); // pushing into the formState
    toggleGifGrid();
  };
Enter fullscreen mode Exit fullscreen mode

More details about useField at: useField doc

The last, but not least, component we will look at is the <CharacterCounter/> component.

⚛️ CharacterCounter

import React from "react";
import { useSelector } from "usetheform";
import { ProgressRingBar } from "./../ProgressRingBar/ProgressRingBar";
import { getProgressRingBarProps } from "./utils/getProgressRingBarProps";

export const CharacterCounter = ({ maxChars }) => {
  const [plainText] = useSelector((state) => state.editor.plainText);

  const props = getProgressRingBarProps(plainText, maxChars);

  return (
    <ProgressRingBar {...props}  />
  );
};
Enter fullscreen mode Exit fullscreen mode

The CharacterCounter component counts the typed characters. In order to do so it uses the useSelector hook for picking the plainText from the form state using the utils function named getProgressRingBarProps .

More details about useSelector at: useSelector doc

Conclusion

Hope you enjoyed reading this post. If you did so, please, use the buttons below to share it. 🙏 Thanks for reading!

Tweet

Linkedin

Top comments (0)