Photo Album

10525 단어 React후크
마지막 날은 Photo Album입니다. 목록의 썸네일을 누르면 썸네일이 해당 위치에서 확대되어 이미지 세부 정보를 표시하는 UI입니다.

code: github/$ yarn 1225







usePhotoDetail



Custom Hooks 고장입니다. 평소와 같이 State, Option, MemoizedStyle, handler로 구성됩니다.

components/usePhotoDetail.ts
const usePhotoDetail = (props: Props) => {
  const [state, update] = useState<State>(defaultState())
  const options = useMemo(...)
  const bgStyle = useMemo(...)
  const photoStyle = useMemo(...)
  const handleTouchMove = useCallback(...)
  const handleOpen = useCallback(...)
  const handleClose = useCallback(...)
  return {
    isOpen: state.isOpen,
    isOpened: state.isOpened,
    bgStyle,
    photoStyle,
    handleOpen,
    handleClose
  }
}

썸네일을 누르면 처리가 혼잡합니다. 누름 썸네일에서 줌 업 하도록(듯이), 시간차로 상태를 갱신해 CSS 애니메이션을 발화합니다.

components/usePhotoDetail.ts
const handleOpen = useCallback(
  (e: MouseEvent<HTMLElement>) => {
    // 押下された要素矩形を取得
    const rect = ...
    // アニメーション要素矩形初期値をセット
    update(...)
    // CSS アニメーション発火
    setTimeout(() => {
      update(_state => ({...}))
    }, 16) 
    // 詳細 View 表示状態にセット
    setTimeout(() => {
      update(_state => ({...}))
    }, options.transitionDuration)
  },
  [state.bgRect, state.photoRect, options]
)

Memoized Component



이미지 줌업의 거동에 3개의 컴퍼넌트를 이용하고 있습니다.
  • 목록 표시 축소판
  • 줌 업 썸네일
  • 확대 표시 이미지

  • 모두 이미지 경로는 동일합니다. iOS safari에서는 이미지 캐시가 남아 있어도 컴포넌트 마운트 시 다시 그려지고 깜박임이 발생합니다. 그 때문에, useMemo 로 MemoizedComponent 를 보관 유지해, 재이용하는 것으로 이것을 회피할 수가 있습니다.

    components/index.tsx
    const View = (props: Props) => {
      const {
        isOpen,
        isOpened,
        bgStyle,
        photoStyle,
        handleOpen,
        handleClose
      } = usePhotoDetail({ transitionDuration })
      const photoComponent = useMemo(
        () => <Photo imgPath={props.imgPath} />,
        [props.imgPath]
      )
      return (
        <div className={props.className}>
          <div className="photo" onClick={handleOpen}>
            {photoComponent}
          </div>
          {isOpen && (
            <Detail
              title={props.title}
              body={props.body}
              bgStyle={bgStyle}
              photoStyle={photoStyle}
              photoComponent={photoComponent}
              isOpened={isOpened}
              transitionDuration={transitionDuration}
              handleClose={handleClose}
            />
          )}
        </div>
      )
    }
    
    Photo 컴퍼넌트는 이렇게 얇습니다. 분할하는 것은 위의 이유와 같습니다.

    comonents/item/photo.tsx
    const View = (props: Props) => (
      <p className={props.className} />
    )
    

    좋은 웹페이지 즐겨찾기