0
0

More than 3 years have passed since last update.

Reactコンポーネントに渡される文字列内のURLをリンクにする方法

Posted at

現状

umiJSのcommentにリンクを挿入する
renderItem={}内のcommentタグがチャットメッセージコンポーネントに該当する。
その中のcontentプロパティにメッセージが渡されている。

問題点

itemを受け取ってtextを表示している。
問題はここが全てstring型で渡されているので、URLを添付してもリンクが繋がらない。

// 修正前
<List
       /* 省略 */
  renderItem={item => {
     return (
         <li>
           <Comment
             author={item.username}
             content={item.text} // <== ここ
            />
        </li>
      )
    }}
 />

実装方針

入力された文字列からURLを検索し変換してからcontentに渡す。
正規表現を用いてURLライクな文字列を抽出し、aタグに置換する。

ブログより借用
const link = (str) => {
    const regexp_url = /https?:\/\/[a-zA-Z0-9.\-_@:/~?%&;=+#',()*!]+/g;
    const regexp_makeLink = (url) => {
        return '<a href="' + url + '">' + url + '</a>';
    }

    return str.replace(regexp_url, regexp_makeLink);
}

strを受け取ってreplace(検索文字, 置換後)メソッドで置換する。
regexp_urlを正規表現で定義して、https://~~~という文字列を検索する。
末尾の/gはテキストないの全てのurlを検索するもので、urlがヒットするたびにregexp_makeLinkメソッドが実行される。

replaceメソッドは第一引数が席表現の場合、第二引数の関数の引数は(文字列, n番目の()で括られた文字列)を受け取る。

regexp_makeLinkは受け取ったregexp_urlをaタグで囲う関数。

そしてautoLink関数で処理したitem.textCommentcontentプロパティに渡す。

components/TextChat/index.tsx
// 修正前
<List
       /* 省略 */ 
  renderItem={item => {
     return (
         <li>
           <Comment
             author={item.username}
             content={link(item.text)} // <== ここ
            />
        </li>
      )
    }}
 />

この時点で予期しない挙動に気づく...

<a href="' + url + '">' + url + '</a>がそのまま文字列として表示されてしまう。
現状は全て文字列として渡されてしまっているものをJSX.Elementとして渡さないといけない。

セキュリティ上あまり良くないような気がするが、InnerHTMLで埋め込む方針をとる。
Reactの場合、dangerouslySetInnerHTMLという書き方になる。

dangerouslySetInnerHTMLでaタグを認識させる。

  const link = (str: string): JSX.Element => {
    const regexp_url = /https?:\/\/[a-zA-Z0-9.\-_@:/~?%&;=+#',()*!]+/g;
    const regexp_makeLink = (url: string): string => {
      return `<a href=${url} target="_blank" rel="noopener noreferrer">${url}</a>`;
    }
    const replacedString = str.replace(regexp_url, regexp_makeLink);

    return (
      <p dangerouslySetInnerHTML={{__html: replacedString}}></p>
    )
  };

__htmlプロパティに先ほど変換したURLを含む全ての文字列を渡すことで、pタグのなかにaタグを含む文字列を入れる。
ちなみに新規タブで開きたかったのでtarget="_blank"を追記。

加えて、「開かれた新規タブ側で、リンク元タブの URL を操作できてしまう」という虚弱性対策としてrel="noopener noreferrerを加える。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0