以前こちらの記事でMarkdownをHTML化して表示するための方法をいくつか紹介しました。
その中で開発中のアプリで使っているHTMLViewはいけてないですね、という話を書きました。
が、色々調べているうちに、ちょっと使い方が間違っていたのではということに気がついたのでアップデートしておきます。
valueの渡しかた
HTMLView
はvalue
プロパティを受け取ります。なのでこれまではMarkdownから生成されたHTMLをそのまま渡していました。
<HTMLView value={html} />
公式READMEのサンプルコードをみてもこうなっています。
render() {
const htmlContent = `<p><a href="http://jsdf.co">♥ nice job!</a></p>`;
return (
<HTMLView
value={htmlContent}
stylesheet={styles}
/>
);
}
でもこれだと妙に行間が広かったりとか画像が見えなかったりとかして、質が悪い感じがしていました。 うまくスタイルも効かせることができずに困っていました。
あるときIssueか何かを見ていてコード例が載っていたんですが、それを真似てHTMLをhtml
タグとbody
タグで囲んでみたら妙に広い行間や画像が見えない件が解決しました。
<HTMLView value={`<html><body>${html}</body></html>`} />
コードを見た感じではhtml
タグやbody
タグを扱っているようには見えなかったので、なぜ?という気がしますが、変更後のほうが良さそうに見えるのでこちらを使うように修正したいと考えています。
こちらのサンプルにおけるbefore afterの画像を載せておきます。
before
After
残念ながら入れ子の<ul>
はうまくいきません。中のコードを見ても考慮されていないので対応するにはライブラリ本体に手を入れる必要があるかと。
renderNodeで各DOMをカスタマイズ
先程の例のAfterを見ると画像の横幅がはみ出ています。これを直すためにrenderNode
関数を渡して、表示するコンポーネントをカスタマイズできるようです。
renderNode
に関してはREADMEに載ってるので完全に僕がサボってたわけですが、便利ですね。renderNode
必須かもな、という気がしています。
先程の例のような画像はみだしの場合は、横幅を限定するようなstyleをあてたImage
コンポーネントを使ってやれば解決します。
もともとこうだったのを
<HTMLView
value={`<html><body>${html}</body></html>`}
/>
こっちにします。
<HTMLView
value={`<html><body>${html}</body></html>`}
renderNode={this.renderNode.bind(this)}
/>
this.renderNode
の実装は例えばこんな感じです。
const { width } = Dimensions.get('window');
renderNode(node, index, siblings, parent, _) {
if (node.name === 'img') {
const { src } = node.attribs;
return (
<Image
key={index}
style={{ width: width * 0.9, height: 300 }}
source={{ uri: src }}
resizeMode="contain"
/>
);
}
}
img
タグの場合には、幅をデバイスの90%に、高さを適当な値に固定したImage
コンポーネントを表示してねというのを書いておきます。
そしたらこうなります。横幅がちゃんと画面に収まりました。
おわり
<ul>
が惜しいですが、いい感じになってきました。
実際のプロジェクトでは同じような設定で使うことが多い気がするので、renderNodeなどを共通してやってくれるコンポーネントを1つ挟むことで利用するのが良さそうです。