Reactあるある!?画像読み込み時のスタイル崩れを解決する
タイトルの内容そのまんまなのですが、みなさんこういう経験ありませんでしょうか?
僕のブログ場合は、プロフィールのカルキチアイコンがコンポーネントがレンダリングされた後に、スタイルが読み込まれるからなのか、カルキチが巨大化(正確には保存時の大きさで画像が読み込まれる)→時間差でスタイルが読み込みこまれる→元の大きさに戻るというカオスなことになっていたので、解決策を考えました。
今回は正攻法なのかどうかはわかりませんが、この現象の解決策を見つけたのでそれについて書いていきます。
解決した方法
react-lazy-load-image-componentというライブラリを使って解決しました。
以下公式の引用です。
React Component to lazy load images and other components/elements. Supports IntersectionObserver and includes a HOC to track window scroll position to improve performance.
引用:https://www.npmjs.com/package/react-lazy-load-image-component
画像やその他のReactコンポーネントや要素を遅延読み込みさせることができる??的な意味だと思います。
→僕の英語力は英検3級レベルで止まっています。センター英語は100点切るか切らないかの瀬戸際でした。。。
プロフィール部分のコンポーネントはこんな感じになっています。
import React from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import { H4 } from '../../styled-components/atoms/Heading';
import { SidebarBox } from '../../styled-components/BlogSidebar';
const Profile: React.FC = () => {
return(
<SidebarBox>
<H4>プロフィール</H4>
<div className="profile-area">
<LazyLoadImage
className="profile-icon"
src='/icon.png'
alt='icon'
effect="blur"
/>
<p>カルキチ副島です。</p>
<p>都内でウェブ系の開発やっています。</p>
<p>普段開発しているものや、日常について書いています。</p>
<p>よろぴ</p>
</div>
</SidebarBox>
);
}
export default Profile;
公式まんまです。
何も特殊なことはしていません。
effect="blur"のスタイルは、アプリケーションの<html>および<body>タグを拡張することができるDocumentコンポーネントで読み込んでいます。
import React from 'react';
import Document, { DocumentContext, Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
class MyDocument extends Document {
static async getInitialProps(context: DocumentContext) {
const sheet = new ServerStyleSheet();
const originalRenderPage = context.renderPage;
context.renderPage = () =>
originalRenderPage({
enhanceApp: (App: any) => (props :any) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(context);
return {
...initialProps,
styles: [...(initialProps.styles as any), ...sheet.getStyleElement()]
};
};
public render() {
return (
<Html lang="ja">
<Head>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<style>{
`html,body,h1,h2,h3,h4,p {
padding: 0;
margin: 0;
}
body {
font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,YuGothic,ヒラギノ角ゴ ProN W3,Hiragino Kaku Gothic ProN,Arial,メイリオ,Meiryo,sans-serif;
line-height: 1.5;
color: #2b2c30;
}
a {
text-decoration: none;
}
.lazy-load-image-background.blur {
filter: blur(15px);
}
.lazy-load-image-background.blur.lazy-load-image-loaded {
filter: blur(0);
transition: filter .3s;
}
.lazy-load-image-background.blur > img {
opacity: 0;
}
.lazy-load-image-background.blur.lazy-load-image-loaded > img {
opacity: 1;
transition: opacity .3s;
}
`
}</style>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
公式ではコンポーネント内部でスタイルを読み込んでいましたが、TypeScriptではエラーが起きたので、headで読み込むようにしています。
僕は面倒臭がりなので、リセットCSSもここで指定しています。
ちなみに、プロフィールは関数コンポーネントなのに、_document.tsxはクラスコンポーネントになっている理由は、Documentコンポーネントを使用する場合はNext.jsの仕様上、NextDocumentというクラスを継承しないと使えないからです。
GithubのNext.jsで作られたアプリケーションのリポジトリを流し見た感じ、Documentコンポーネントを関数コンポーネントで書いていたものはなかったです。
公式もクラスコンポーネントになっていました。
【Next.jsの公式】
https://nextjs.org/docs/advanced-features/custom-document
これはおそらくなので、書き方次第ではもしかしたら関数コンポーネントでもいけるかもしれないです。
まとめ
特に何か面倒なことをしなくても、画像の遅延読み込みができるので、結構良きって感じです。
ただ、コンポーネントや要素しか効かなそうなので、Markdown内の画像に対して、個別に遅延読み込みをかけるとかは多分無理だと思います。
特定の画像に対してや、JSONオブジェクトにある画像のパスを取得して、mapとかでループさせつつ、取得した画像に対して遅延読み込みをかけるというのが主なユースケースなのかなって感じです。
おまけ
全体的にまだまだ作りが甘い部分が多いので、もうちょい頑張りたいです。
特に記事の部分は、もうちょい読みやすくしたいものです。
あと日本語力もつけたいです。
もう若人とは言えない年齢なのにマジとかヤバイ、パナイなど若者言葉が抜けないです。
あーマジヤバ