スクロール位置に応じて画像を読み込み・解放する処理を、ここまでのスクロール Hooks テクニックで実装します。
code: github / $ yarn 1210
挙動の確認
画面表示領域に入ってから各画像はロード開始されますが、手元で確認するには直ぐにレスポンスが返ってくるため、あまり効果が分かりません。Chrome で低速環境デバッグを有効にしてから確認すると分かりやすいです。
memoize と render props
Hooks API はループや条件分岐では利用出来ない縛りがあります。この時、お馴染みの render props が役にたちます。
const View = (props: Props) => (
<div className={props.className}>
{Assets.map((asset, index) => (
<Item
key={index}
imgPath={asset.imgPath}
render={({
imgSrc,
loaded,
loadCompleted,
handleLoadCompleted
}) => (
<>
<Photo
imgSrc={imgSrc}
loaded={loaded}
loadCompleted={loadCompleted}
onTransitionEnd={handleLoadCompleted}
/>
<Description
title={asset.title}
body={asset.body}
/>
</>
)}
/>
))}
</div>
)
こちらがループ処理にかけられる Item コンポーネントです。Custom Hooks を利用し、得られた state を memoize。状態が変化した時に限り rerender props します。
export default (props: Props) => {
const {
state,
handleLoadStart,
handleDispose,
handleLoadCompleted
} = useImageLoader({
imgPath: props.imgPath
})
return useMemo(
() => (
<ScrollWrapper
onEnter={handleLoadStart}
onLeave={handleDispose}
>
{props.render({
imgSrc: state.img.src,
loaded: state.loaded,
loadCompleted: state.loadCompleted,
handleLoadCompleted
})}
</ScrollWrapper>
),
[state]
)
}
useImageLoader
画像の読み込み解放を行う Custom Hooks です。責務がはっきりしてるので、再利用性の高い Custom Hooks といえます。
const useImageLoader = (props: Props) => {
const [state, update] = useState({
img: new Image(),
loaded: false,
loadCompleted: false
})
const handleLoadStart = useCallback(
() => {
state.img.onload = () =>
update(_state => ({ ..._state, loaded: true }))
state.img.src = props.imgPath
},
[state.img]
)
const handleDispose = useCallback(() => {
update(_state => ({
..._state,
img: new Image(),
loaded: false,
loadCompleted: false
}))
}, [])
const handleLoadCompleted = useCallback(() => {
update(_state => ({
..._state,
loadCompleted: true
}))
}, [])
return {
state,
handleLoadStart,
handleDispose,
handleLoadCompleted
}
}
最後に、ここから得られたハンドラを、スクロール処理を行う Hooks Component にバインドします。このコンポーネントは先日の投稿で利用しているものと殆ど同じです。毎度のものなので、本日は割愛します。
<ScrollWrapper
onEnter={handleLoadStart}
onLeave={handleDispose}
>
...
</ScrollWrapper>