絵文字ピッカー emoji-picker-react の導入方法まとめ
概要
スマートフォンの絵文字選択キーボードのような絵文字選択画面を、ブラウザ上で実現できるemoji-picker-react
というライブラリについて、導入方法をまとめました。
emoji-picker-react
は、名前の通りReact向けのライブラリとなります。
Next.jsにも導入可能ですが、1点注意事項があり後述します。
デモ
導入方法
インストール
npm i emoji-picker-react
最小構成のサンプルコード
import React, { useState } from 'react';
import Picker from 'emoji-picker-react';
const App = () => {
const [chosenEmoji, setChosenEmoji] = useState(null);
const onEmojiClick = (event, emojiObject) => {
setChosenEmoji(emojiObject);
};
return (
<div>
{chosenEmoji ? (
<span>You chose: {chosenEmoji.emoji}</span>
) : (
<span>No emoji Chosen</span>
)}
<Picker onEmojiClick={onEmojiClick} />
</div>
);
};
chosenEmojiのオブジェクトの構造は次のようになっています。
emoji
キーにアクセスすることで、選択された絵文字のStringを取得することができます。
{
activeSkinTone: "neutral",
emoji: "🙂",
names: ["slightly smiling face", "slightly_smiling_face"],
originalUnified: "1f642",
unified: "1f642",
}
その他オプション
Pickerコンポーネントに設定可能なオプション一覧は公式サイトに載っています。
onEmojiClick
以外はあまり使うことは無いかなと思いました。
Next.jsに導入する際の注意点
上記はReactでのサンプルコードですが、そのままNext.jsに導入しようとすると次のようなエラーが発生します。
Server Error
ReferenceError: document is not defined
どうやら、このライブラリ内でwindow オブジェクトを使用しているようですが、window オブジェクトはクライアント ( ブラウザ ) 側でしか動作しないため、同時にサーバー側でもレンダリングを行う Next.js ではこれを処理できないことが原因のエラーです。
エラーの回避策として、ライブラリのインポート方法を次のように変更します。
import dynamic from "next/dynamic";
const Picker = dynamic(() => import("emoji-picker-react"), { ssr: false });
Dynamic Import
という ES2020の機能があり、Next.jsでもサポートされています。
これに{ssr: false}
というオプションを設定することで、Next.jsがこのモジュールはSSRしないものと判断し、ブラウザ側だけで実行されるため、エラーを回避することができます。
応用例
私の個人WEBサイトのコメント機能に、絵文字のアイキャッチも合わせて投稿する機能を実装してみました。絵文字はString型のため扱いも難しくありません。
コメントに絵文字のアイキャッチがつくと、なんだか楽しい雰囲気になって良いと思いました!
Before
After
サンプルコード
上記応用例の絵文字ピッカー箇所を抜粋したNext.jsでのサンプルコードです。
ピッカー画面外をクリックしたときに、ピッカー画面を閉じる処理を入れています。
import { IEmojiData } from "emoji-picker-react";
import dynamic from "next/dynamic";
import { useEffect, useRef, useState } from "react";
const Picker = dynamic(() => import("emoji-picker-react"), { ssr: false });
interface Props_EmojiSelectBox {
inputHtmlId?: string;
}
const initialEmoji: IEmojiData = {
activeSkinTone: "neutral",
emoji: "🙂",
names: ["slightly smiling face", "slightly_smiling_face"],
originalUnified: "1f642",
unified: "1f642",
};
const EmojiSelectBox: React.FC<Props_EmojiSelectBox> = (props) => {
const { inputHtmlId } = props;
const inputRef = useRef<HTMLDivElement>(null);
const [showPicker, setShowPicker] = useState(false);
const [chosenEmoji, setChosenEmoji] = useState<IEmojiData>(initialEmoji);
const onEmojiClick = (event: React.MouseEvent, emojiObject: IEmojiData) => {
setChosenEmoji(emojiObject);
};
// 絵文字Pickerの表示/非表示のイベントを作成
useEffect(() => {
const el = inputRef.current;
const hundleClick = (e: MouseEvent) => {
// 絵文字Pickerをクリックした時の処理
if (el?.contains(e.target as Node)) {
setShowPicker(true);
}
// 絵文字Picker以外をクリックした時の処理
else {
setShowPicker(false); // 絵文字Pickerを非表示化
}
};
// ウインドウ全体のクリックイベントを作成
document.addEventListener("click", hundleClick);
// コンポーネントのアンマウント・再レンダリング時に、クリックイベントを削除
return () => {
document.removeEventListener("click", hundleClick);
};
}, [inputRef]);
return (
<>
<div className="flex flex-wrap -mx-3 mb-4">
<div className="w-full px-3 relative" ref={inputRef}>
<label
className="block tracking-wide text-gray-700 text-xs font-bold mb-2"
htmlFor={inputHtmlId}
>
絵文字
</label>
<input
className={
"block w-full h-12 text-3xl bg-gray-200 text-gray-700 border rounded px-4 mb-3 focus:outline-none focus:bg-white focus:border-gray-500"
}
type="button"
id={inputHtmlId}
value={chosenEmoji.emoji}
/>
<div className={"absolute bottom-16" + (showPicker ? "" : " hidden")}>
<Picker onEmojiClick={onEmojiClick} />
</div>
</div>
</div>
</>
);
};
export default EmojiSelectBox;
参考文献
Discussion