テキストが省略されているかどうかの判定を行ってくれる React カスタムフックを作る


こんにちは、 Gaji-Labo アシスタントエンジニアの石垣です。

今回は、テキストが省略されているかどうかの判定を行ってくれるReact カスタムフックの作り方をまとめたいと思います。

今回ご紹介する実装は、CodeSandbox でも触ることができますので是非ご覧になってみてください。

実現したいこと

一定以上の長さで省略表示されるテキストコンポーネントで、省略時はホバーでツールチップを表示し、省略されていない時は何もしないという実装をしたいことがあるかと思います。

ホバーすると省略前のテキストをツールチップで表示する
全文表示されている時はツールチップを表示しない

そんな時、テキストが省略されているかどうかをチェックしてくれるカスタムフックがあると便利です。

useTruncated を作る

まずは実際にカスタムフックを作ってみます。

CodeSandbox

import { useState, useCallback } from "react";

export const useTruncated = (
  text?: string,
  truncateMaxWidth?: number
): [(node: HTMLDivElement) => void, boolean | undefined] => {
  const [truncated, setTruncated] = useState(false);
  const ref = useCallback(
    (node) => {
      if (!node) return;
      if (truncateMaxWidth) {
        setTruncated(node.scrollWidth > truncateMaxWidth);
      } else {
        setTruncated(node.scrollWidth > node.offsetWidth);
      }
    },
    // NOTE: 入ってくる text の変更で truncate の判定を行うため無効化
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [text, truncateMaxWidth]
  );
  return [ref, truncated];
};

簡単にご説明しますと、

  • text: 省略対象のテキスト
  • truncateMaxWidth: 省略対象の要素の最大幅(これを超えると省略される)

を受け取り、

  • ref: 省略対象のテキストを内包するコンポーネント(ここで省略されているかどうかを判定する)
  • truncated: 省略されているかどうか

を返すカスタムフックです。

ref の中身を変更すると、まず ref の offsetWidth と truncateMaxWidth もしくは scrollWidth を取得します。

offsetWidth はブラウザ上に表示されている(省略された)要素の幅、scrollWidth は実際の要素の幅です。

これらを取得することで省略されているかどうかを判定することができます。

useTruncated を使う

上記のカスタムフックを使って、省略時にはツールチップを表示し、非省略時には何も起きないコンポーネントを作ってみます。

CodeSandbox

import { ReactElement } from "react";
import { useTruncated } from "./hooks";
import { Tooltip } from "@material-ui/core";
import "./styles.css";

const TRUNCATE_MAX_WIDTH = 300;

type TruncatableTextProps = {
  text: string;
};

function TruncatableText({ text }: TruncatableTextProps): ReactElement | null {
  const [ref, truncated] = useTruncated(text, TRUNCATE_MAX_WIDTH);

  return (
    <>
      {text && (
        <div
          style={{
            backgroundColor: "lightsalmon"
          }}
        >
          {truncated ? (
            <Tooltip title={text}>
              <div
                ref={ref}
                style={{
                  width: `${TRUNCATE_MAX_WIDTH}px`,
                  overflow: "hidden",
                  whiteSpace: "nowrap",
                  textOverflow: "ellipsis",
                  textAlign: "left",
                  backgroundColor: "pink"
                }}
              >
                {text}
              </div>
            </Tooltip>
          ) : (
            <div
              ref={ref}
              style={{
                width: `${TRUNCATE_MAX_WIDTH}px`,
                overflow: "hidden",
                whiteSpace: "nowrap",
                textOverflow: "ellipsis",
                textAlign: "left",
                backgroundColor: "lime"
              }}
            >
              {text}
            </div>
          )}
        </div>
      )}
    </>
  );
}

export default function App() {
  return (
    <div className="App">
      <TruncatableText text="ダミーテキストダミーテキストダミーテキスト" />
    </div>
  );
}

TruncatableText が本体のコンポーネントです。省略対象のテキストを渡し、TRUNCATE_MAX_WIDTH の幅を超えると省略表示し、かつツールチップを表示するようにするものです。

TruncatableText の text を変更するとツールチップの表示非表示が切り替わる様子のキャプチャ

TruncatableText の text を変更してみると、省略されているか否かに応じてスタイルとツールチップの表示有無が切り替わるようになっています。

これでテキストが省略されているかどうかをチェックしてくれるカスタムフックと、そのコンポーネントを実装することができました。

まとめ

今回はテキストが省略されているかどうかの判定を行ってくれるReact カスタムフックの作り方をまとめました。

参考にしていただけると幸いです。

Gaji-Laboでは、React経験が豊富なフロントエンドエンジニアを募集しています

弊社ではReactの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!

求人応募してみる!

投稿者 Ishigaki Shotaro

未経験から Gaji-Labo に入社。現在は React/TypeScript/Next.js の案件で MUI を使ったコンポーネント組み込みを担当。プロジェクトチームのリードとして共に組み込み作業をしているメンバーの進行管理も行っています。休日はだいたい家で音楽を聴いており、たまにライブに出かけています。