HTMLタグ要素を含む文字列をレンダリングする方法
最近では、検索・置換の需要が高まっており、下図のようにキーワード情報を入力し、対応するデータに合わせてキーワードをマークして表示することができます。
上記の要件を実現するためのアイデアは、フロントエンドが検索内容を判断し、その内容内の対応するキーワードの背景色のスタイルを設定することです。
if (content?.includes(serachV)) {
content = content.replaceAll(serachV, `<span style="background-color: gold;">${
serachV}</span>`);
return content
}
しかし、この設定を行っても思ったような効果が得られず、フロントエンドのディスプレイが黄色にならず、効果が得られませんでした。
情報を調べた結果、React ではセキュリティ上の理由 (XSS 攻撃) により、React.js に挿入されたすべての式が自動的にエスケープされることがわかりました。これは jQuery の text(…) 関数に相当し、HTML の書式設定はすべて逃げた。
したがって、リッチ テキスト エディターで操作した後のコンテンツは、次の図の赤いボックスに示すように、元のラベル スタイルが保持され、正しく表示できません。
したがって、この API を使用してdangerouslySetInnerHTML
内部 HTML として設定し、上記の効果を実現できます。コードは次のとおりです。
if (content?.includes(serachV)) {
content = content.replaceAll(serachV, `<span style="background-color: gold;">${
serachV}</span>`);
return <span dangerouslySetInnerHTML={
{
__html: content }} />;
}
完全なコードは次のとおりです。
export const quickReplaceColumns = (serachV: string) => {
const renderNode = (content: string) => {
if (content?.includes(serachV)) {
content = content.replaceAll(serachV, `<span style="background-color: gold;">${
serachV}</span>`);
return <span dangerouslySetInnerHTML={
{
__html: content }} />;
}
return content || '--';
};
return [
{
title: '用例名称',
dataIndex: 'name',
width: '30%',
render: (name: string) => renderNode(name),
},
{
title: '用例内容',
dataIndex: 'content',
width: '50%',
render: (content: string) => renderNode(content),
},
{
title: '所属模块',
dataIndex: 'module_name',
width: '10%',
},
];
};
効果は次のとおりです。
危険なSetInnerHTMLとは何ですか
危険なほどSetInnerHTMlはReactタグの属性であり、危険なほどSetInnerHTMLの翻訳は、危険な内部HTMLを設定します。
なぜ危険なのでしょうか? ユーザー入力は制御できないため、そのような操作がユーザー入力用に開発された場合、クロスサイト スクリプティング (XSS) 攻撃やその他の Web ページ攻撃につながり、予期しないエラーが表示される可能性があります。
ただし、ここでの使用法は入力を制御し、ユーザー入力を受け付けないため、上記の状況は発生せず、安心して使用できます。
使用上の注意
- 危険なlySetInnerHTMLの構文: 最初の層 { } は JSX 構文を表し、2 番目の層 { } は __html:string のキーと値のペアです。
- 最初、バッククォートなしで
<img className="detail_img" src=${v[0]} />
レンダリングされた結果はすべて[object Object] でした。それに気づくまでに長い時間がかかりました__html:string
- バックティックで囲まれた HTML コードは JSX 構文ではなくなったため、clasName を class に変更する必要があります。