This is a premium alert message you can set from Layout! Get Now!

Build a React comment form with @mention functionality

0

Commenting on threads and messages used to be pretty messy prior to the introduction of @mention functionalities. Though you could send a message in a thread, there was often no way of knowing who the message was for, and there was no way to engage those who weren’t already involved in the conversation.

With the introduction of @mention, you can mention friends (or well-meaning social media experts) and invite them to join the discussion.

You also can find forms with @mention functionalities in various applications like Facebook, Dropbox, WhatsApp, and Gmail.

This article will look at building a form with the @mention functionality included in React. We will specifically be working with the react-mentions package.

You can find the complete code for this tutorial in my Github repo. Let’s get started!

Building a comment form with react-mentions

Let’s start by creating a new React app with the command below:

npx create-react-app react-mentions

If you are using Yarn, run the following command:

yarn create react-app react-mentions

I’ll be using Yarn for the rest of this tutorial.

Next, install the react-mentions package as follows:

yarn add react-mentions

The react-mentions package exports two React components for rendering mentions: the MentionsInput component and the Mention component. MentionsInput is the main component used to render the text area control and can take one or more Mention components as children.

The Mention component represents a data source for a class of mentionable objects, including users, issues, and more.

Using the MentionsInput and Mention components

Let’s implement react-mentions into our application. Head over to the App.js file and replace the entire code with the code block below:

import { Mention, MentionsInput } from "react-mentions";

function App() {
  return (
    <div>
      <h2>Let's get started</h2>
      <MentionsInput>
        <Mention />
      </MentionsInput>
    </div>
  );
}
export default App;

When we start up the development server with yarn start, we should get an input box like in the image below:

Lets Get Started Screen

Next, we will create an array of dummy data that’ll be provided to the Mention component. The data must have id and display as specific keys.

We also need to create a state event. This will be used to bind the state of our application to the values coming from the data and then pass it to the MentionsInput component.

Copy and paste the code below into the App.js file:

function App() {
  const [value, setValue] = useState("");

  const users = [
    {
      id: "isaac",
      display: "Isaac Newton",
    },
    {
      id: "sam",
      display: "Sam Victor",
    },
    {
      id: "emma",
      display: "emmanuel@nobody.com",
    },
  ];

  ...
}

We created a state variable and user array based on the code block above. The user array contains objects with id and display parameters. These are the parameters needed to populate the react-mentions component.

Now, let’s update the return() statement with the code below:

  return (
    <div className="App">
      <MentionsInput
        value={value}
        onChange={(e) => setValue(e.target.value)}>

        <Mention
          data={users} />
      </MentionsInput>
    </div>
  );

We are using the MentionsInput tag that takes in the value prop. We’re then setting the state value with the onChange prop. With all this done, we should be able to achieve this:

Setting The State Value With The Onchange Prop

Styling react-mentions components

Looking at our progress above, you may notice that our component looks a bit out of place. We can fix it by customizing with styles.

Create a mentionStyles.js file in the src folder and paste the code below:

export default {
  backgroundColor: "#cee4e5",
};

Create a mentionsInputStyles.js file in the src folder as well and paste the code block below into it:

export default {
  control: {
    backgroundColor: '#fff',
    fontSize: 16,
    // fontWeight: 'normal',
  },
  '&multiLine': {
    control: {
      fontFamily: 'monospace',
      minHeight: 63,
    },
    highlighter: {
      padding: 9,
      border: '1px solid transparent',
    },
    input: {
      padding: 9,
      border: '1px solid silver',
    },
  },
  '&singleLine': {
    display: 'inline-block',
    width: 180,
    highlighter: {
      padding: 1,
      border: '2px inset transparent',
    },
    input: {
      padding: 1,
      border: '2px inset',
    },
  },
  suggestions: {
    list: {
      backgroundColor: 'white',
      border: '1px solid rgba(0,0,0,0.15)',
      fontSize: 16,
    },
    item: {
      padding: '5px 15px',
      borderBottom: '1px solid rgba(0,0,0,0.15)',
      '&focused': {
        backgroundColor: '#cee4e5',
      },
    },
  },
}

Head back to App.js and import the style:

import mentionStyle from "./mentionStyle";
import mentionsInputStyle from "./mentionsInputStyle";

Now, update the components:

    <div className="App">
      <MentionsInput
        style={mentionsInputStyle} 
        value={value}
        onChange={(e) => setValue(e.target.value)}>

        <Mention
          style={mentionStyle}
          data={users}
        />
      </MentionsInput>
    </div>

We’ve updated our components by adding the style prop and setting it to the imported style.

With our progress so far, we’ve achieved a nice, customized Mention functionality in our app!

Mention Prop Styled

Exploring other functionalities in react-mentions

The react-mentions package comes with many customizable features, so let’s take a look into some of them!

singleLine input

singleLine input is called when we want our input to be a single line of text rather than the default text area. You can see this in the code below:

return (
 <div className="App">
  ...
  <h2>Using a Single line Input</h2>
      <MentionsInput
        singleLine  //this sets the single line input to true
        style={mentionsInputStyle}
        value={value}
        onChange={(e) => setValue(e.target.value)}
      >

    </div>
  );

Single Line Input

Multiple trigger patterns

We can also decide to use more than one trigger pattern instead of the default @ trigger pattern. Luckily, the react-mention package supports this.

Let’s initiate a second trigger pattern. Import the useCallback hook in the App.js file. The useCallback hook is used to stop the Mention component from rerendering without need:

import { useState, useCallback } from "react";

Next, create an email validation regex. This will act as an additional trigger that detects if the input is an email. It will then highlight it as a mention.

function App() {
  const [value, setValue] = useState("");
  const emailRegex = /(([^\s@]+@[^\s@]+\.[^\s@]+))$/;

  ...
  return (
    <div className="App">
      <h2>Using Multiple trigger patterns</h2>  
      <MentionsInput
        style={mentionsInputStyle}
        value={value}
        onChange={(e) => setValue(e.target.value)}
        >

        <Mention style={mentionStyle} data={users} />

        <Mention
          trigger={emailRegex}
          data={(search) => [{ id: search, display: search }]}
          onAdd={useCallback((...args) => {
            console.log(...args);
          }, [])}
          style=
        />
      </MentionsInput>
    </div>
  );

Multiple Trigger Patterns

Modifying the displaying id

The react-mentions library also allows us to change the default displaying id to our preferred one. We can achieve this by using the displayTransform parameter.

   <h2>Displaying ID</h2>
      <MentionsInput
        style={mentionsInputStyle}
        value={value}
        onChange={(e) => setValue(e.target.value)}
      >
        <Mention
          displayTransform={(id) => `<!--${id}-->`}
          style={mentionStyle}
          data={users}
        />
      </MentionsInput>

In the code block above, we return the id from the user object and render it.

Displaying ID

Scrollable text area

Text areas are responsive input fields that adjust in height based on multiple user inputs. This feature can result in a distorted UI and applies to our react-mentions component. We’ll be making our text area scrollable to avoid this distortion and create a nicer UI instead.

First, we’ll import the merge function from the lodash library into the App.js file:

import merge from 'lodash/merge';

The merge function will be responsible for merging our mentionsInputStyle with our new custom style.

function App() {
  let customStyle = merge({}, mentionsInputStyle, {
    input: {
      height: 80,
      overflow: "auto",
    },
    highlighter: {
      height: 80,
      overflow: "hidden",
      boxSizing: "border-box",
    },
  });

  ...
  return (
      <MentionsInput
        value={value}
        onChange={(e) => setValue(e.target.value)}
        style={customStyle}
        placeholder={"Mention people using '@'"}
        a11ySuggestionsListLabel={"Suggested mentions"}
      >
        <Mention
          trigger="@"
          data={users}
          style={mentionStyle}
        />
  );
}

In the code block above, we’re merging the mentionsInputStyle to our newly updated style. We’re also setting the height and width of the text area to a fixed value and automatically setting the overflow.

With that done, we will have a nicer UI with a scrollable component, as shown below:

Scrollable Component

Fetching responses from external sources

In this section, we’ll look at how we can use data from an API in our form. In many cases, our data may be coming from an external source. Let’s see how we handle our responses and add them to the react-mentions data property.

We’ll be working with and fetching users from the JSON Placeholder API for this demo. Copy and paste the code block below into the App.js file:

  function fetchUsers(query, callback) {
    if (!query) return;
    fetch(`https://jsonplaceholder.typicode.com/users?q=${query}`, {
      json: true,
    })
      .then((res) => res.json())
      // Transform the users to what react-mentions expects
      .then((res) => 
        res.map((user) => ({ display: user.username, id: user.name }))
      )

      .then(callback);
  }

Based on the code block above, we’re making an API call to the jsonplaceholder server. We passed two arguments into the fetch function: query and callback.

The query argument holds the input from the mentionInput, while the callback argument is called when we have the response ready.

Next, we are returning a list of users, looping through it, and returning the user’s name and username as an object of display and id.

Finally, we’re calling our function in the data property of the MentionsInput component and displaying the id:

   <MentionsInput
        value={value}
        onChange={(e) => setValue(e.target.value)}
        style={mentionsInputStyle}
        placeholder="Mention any JsonPlaceholder username by typing `@` followed by at least one character"
        a11ySuggestionsListLabel={"Suggested JsonPlaceholder username for mention"}
      >
        <Mention
          displayTransform={(id) => `@${id}`}
          trigger="@"
          data={fetchUsers}
          style={mentionStyle}
        />
      </MentionsInput>

External Sources

Fetching emojis

With the react-mentions package, not only can names be referenced and mentioned, emojis can be mentioned too!

Let’s take a look at how to fetch emojis from an external API and display them in the input field when they’re searched.

function App() {
  const [emojiValue, setEmojiValue] = useState([]);
  const notMatchingRegex = /($a)/;

  useEffect(() => {
    fetch(
      "https://gist.githubusercontent.com/oliveratgithub/0bf11a9aff0d6da7b46f1490f86a71eb/raw/d8e4b78cfe66862cf3809443c1dba017f37b61db/emojis.json"
    )
      .then((data) => {
        return data.json();
      })
      .then((jsonData) => {
        setEmojiValue(jsonData.emojis);
      });
  }, []);
  const queryEmojis = (query, callback) => {
    if (query.length === 0) return;
    const filterValue = emojiValue
      .filter((emoji) => {
        return emoji.name.indexOf(query.toLowerCase()) > -1;
      })
      .slice(0, 10);
    return filterValue.map(({ emoji }) => ({ id: emoji }));
  };

  ...
  return (
      <h3>Emoji support</h3>
      <MentionsInput
        value={value}
        onChange={(e) => setValue(e.target.value)}
        style={mentionsInputStyle}
        placeholder={"Press '&' for emojis, mention people using '@'"}
      >
        <Mention
          trigger="@"
          displayTransform={(username) => `@${username}`}
          markup="@__id__"
          data={users}
          regex={/@(\S+)/}
          style={mentionStyle}
          appendSpaceOnAdd
        />
        <Mention
          trigger="&"
          markup="__id__"
          regex={notMatchingRegex}
          data={queryEmojis}
        />
      </MentionsInput>
  );
}

Based on the code block above, we’re fetching and storing the emojis from our API in our emojiValue as soon as the page loads. We do this using the useEffect hook and displaying the emojis whenever the user searches specific keywords.

Here, we’re using a double trigger pattern using the & symbol for emojis and the @ symbol for the users array. The notMatchingRegex serves as a filter for non-matching emojis.

Emoji Support

Creating a custom form with @mention functionality

In this section, we will be putting together everything we’ve learned about the react-mentions library to build a comment form.

First, create a CustomForm.jsx file in the src directory and paste in the code below:

// CustomForm.jsx

import { useState } from 'react';
import { Mention, MentionsInput } from 'react-mentions';
import styles from './FormInputStyle.module.css';
import mentionsInputStyle from './mentionsInputStyle';
import mentionStyle from './mentionStyle';
const CustomForm = () => {
  const [formState, setFormState] = useState({
    username: '',
    comment: '',
  });
  const [comments, setComments] = useState([]);
  const users = [
    {
      id: 'isaac',
      display: 'Isaac Newton',
    },
    {
      id: 'sam',
      display: 'Sam Victor',
    },
    {
      id: 'emma',
      display: 'emmanuel@nobody.com',
    },
  ];
  const submit = () => {
    if (formState.username === '' || formState.comment === '') {
      alert('Please fill in all fields');
      return;
    }
    setComments((comments) => [
      ...comments,
      {
        username: formState.username,
        comment: formState.comment,
      },
    ]);
    setFormState({
      username: '',
      comment: '',
    });
  };
  const current = new Date();
  const date = `${current.getDate()}/${
    current.getMonth() + 1
  }/${current.getFullYear()}`;

In the code above, we’re importing the package that we will be using from react-mentions as well as the useState hook for handling the comments and state of the forms.

The form and comment state have also been set and are providing the dummy data for the application. Our submit function checks if the fields are filled and sets the comment state. We now have a date variable that gets the date of the comment.

Now, update the return value with the code below:

return (
    <div className={styles.form}>
      <section className={styles.formCard}>
        <h2 className={styles.formTitle}>Comment Form</h2>
        <input
          type="text"
          value={formState.username}
          onChange={(e) =>
            setFormState({ ...formState, username: e.target.value })
          }
          placeholder="Input Your Name"
        />
        <MentionsInput
          placeholder="Add Comment. Use '@' for mention"
          value={formState.comment}
          onChange={(e) =>
            setFormState({ ...formState, comment: e.target.value })
          }
          style={mentionsInputStyle}
        >
          <Mention style={mentionStyle} data={users} />
        </MentionsInput>
        <button onClick={submit}>Submit</button>
      </section>
      {comments.length === 0 ? (
        null
      ) : (
        <section>
          {comments.map((comment, i) => (
            <div className={styles.commentCard} key={i}>
              <p className={styles.username}>
                {comment.username} on {date}
              </p>
              <h2>{comment.comment}</h2>
            </div>
          ))}
        </section>
      )}
    </div>
  );
};
export default CustomForm;

We are passing the appropriate props to the Mention and MentionInput components and displaying the comments below the form (if there are any).

Great! Next, create a FormInputStyle.module.css for styling and paste the following code into it:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.form {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100vh;
  background-color: #ffa5a5;
}
.formTitle {
  font-size: 2rem;
  color: red;
  margin-bottom: 1rem;
}
input {
  height: 3rem;
  width: 25rem;
  margin-bottom: 1rem;
  padding: 1rem;
  font-size: 18px;
  border: 1px solid silver;
}
.formCard {
  width: 27rem;
  display: flex;
  flex-direction: column;
  background-color: rgb(54, 44, 24);
  padding: 1rem;
}
button {
  border: none;
  border-radius: 3px;
  color: white;
  background-color: green;
  font-size: 1.2rem;
  padding: 10px;
  margin-top: 1rem;
}
.commentCard {
  margin: 1.5rem;
  color: rgb(173, 173, 173);
  font-size: 1rem;
  background-color: #444;
  padding: 1rem;
  width: 27rem;
}
.username {
  color: white;
  font-size: 1.3rem;
}

With that, we are done creating the form! You should see something like this:

Final Form

Conclusion

In this article, we’ve learned about react-mentions, an easy-to-use library for building forms with @mention functionalities. We also looked at the different functionalities of the react-mentions package and how we can use them. We also built a comment form with @mention functionality using the react-mention package.

Thanks for reading!

The post Build a React comment form with @mention functionality appeared first on LogRocket Blog.



from LogRocket Blog https://ift.tt/NjF8pmW
Gain $200 in a week
via Read more

Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.
Post a Comment

Search This Blog

To Top