embed.jsを利用すればリッチなコンテンツ表現ができます。embed.jsは特定のDOM要素を直接操作して、コンテンツをリッチ化するものと、リッチなコンテンツを文字列として返すものの二種類のAPIがあります。
Reactで使う場合、DOMを直接操作してしますと、VituralDOMの管理外のものが混在して、メモリ管理などが面倒になります。今回は、文字列として取得するAPIを利用してReactからembed.jsを利用します。
import
import 'embed.js';
ES6の記法で書いています。僕の環境ではimport EmbedJS from 'embed.js';のように記述できなかったので、全部をインポートしています。
embed.jsの利用
embed.jsで文字列として返還後の文字列を取得します。そのあと、dangerouslySetInnerHTMLを使ってコンテンツを表示します。コードはこんな感じ。
export default class Body extends React.Component {
componentDidMount() {
this.mounted = true;
if (this.props.body && !this.state.body) {
const options = {
element: this.refs.body,
};
new window.EmbedJS(options)
.text((htmlBody) => {
if (!this.mounted) {
return;
}
this.setBody(htmlBody);
});
}
}
componentWillUnmount() {
this.mounted = false;
}
setBody(htmlBody) {
this.setState({body: (<div dangerouslySetInnerHTML={{__html: htmlBody}} />)});
}
render() {
return this.state.body ? this.state.body : (<div ref="body">this.props.body</div>);
}
}
componentDidMountで、EmbedJSでコンテンツを変換します。コンストラクタに渡すオブジェクトでelement(DOM要素)を指定します。elementじゃなく、文字列で指定も一応は可能なんですが、
- 文字列のエスケープを事前に自前でやっておかないとXSSの脆弱性になる
- Twitterの埋め込みなどはelementで指定しないとエラーになる
など問題があるので、文字列じゃなくelementで指定するのがいいです。
そのあとに、textメッソドを実行して文字列に変換しています。textメソッドは非同期実行されて結果が返ってくるので、結果が帰る前にunmountされると警告がコンソールにでるので、unmountされそうになったらstateを更新しないようにしています。
mount状態を取得するisMountedは昔のReactは使えたのですが今はdeprecatedになっっているので、使わないで自前でmount状態を管理します。
でわでわ