ChungHa Brocher_5 : 2021.06.06 Change Promise to Array , using 'useEffect'

4753 ワード

Status Quo


1) PicturesData

export const PictureDatas = (async function () { 
  const picutureDatas = await Axios.post('/api/album/images')
  .then(res=> {
      const pictures = res.data.imgDatas.slice()
      // shuffle Array
      for (let i = pictures.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [pictures[i], pictures[j]] = [pictures[j], pictures[i]];
      }
      return pictures
  })
  return picutureDatas
})()

2) GetpictureData ~~

export default function GetPictureData(pictureNum: number) {
~ ~
~ ~
  const PictureDatasLength = PictureDatas.length - 1;
  ~~
 }

Error

 Promise<any> does not have 'length' in it's form 

Purpose


have to make return type as normal array so that it has 'length' attribute in it

Solution


1) Put "getPictsFromDB" Func that retrieves data from db into "GetPictureData" Component


2) Make it as async function in order to apply 'await' to axios and wait until all the datas are retrieved


3) Apply 'useCallback' to "getPictsFromDB" Func, since we are going to put it to 'dependency array'


4) Put it into 1st useEffect


below code will run as soon as the component renders with getPictsFromDB() in the dependency array ,
since we applied 'useCallback' to 'getPictsFromDB()' ,
getPictsFromDB() will not change, which means
below "useEffect()"will only run at initial moment

5) Put retrieved data (pictureDatas) into 2nd useEffect dependency Array


At the initial rendering, there will be no data in 'pictureDatas', since < 1st useEffect > has not run yet,
but as soon as 1st useEffect runs , actual picture datas will be set to "pictureDatas", which means 2nd useEffect is able to render the pictures to pages
  useEffect(() => {
    getPictsFromDB();
  }, [getPictsFromDB]);

Full Code

import { useEffect, useState, useCallback } from "react";
import { ImageDataType } from "assets/data/types";
import Axios from "axios";

export default function GetPictureData(pictureNum: number) {
  const [loading, setLoading] = useState(true);
  const [pictureDatas, setPictureDatas] = useState<Array<any>>([]);
  const [pictures, setPictures] = useState<Array<ImageDataType>>([]);
  const [curPictNum, setCurPictNum] = useState(0);
  const [hasMore, setHasMore] = useState(false);
  const PictureDatasLength = pictureDatas.length;

  const getPictsFromDB = useCallback(
    async function () {
      const datas: Array<any> = await Axios.post("/api/album/images").then(
        (res) => res.data.imgDatas
      );
      const pictures = datas.slice();
      // shuffle Array
      for (let i = pictures.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [pictures[i], pictures[j]] = [pictures[j], pictures[i]];
      }
      setPictureDatas(pictures);
    },
    [setPictureDatas]
  ); // 변하지 않게 해주려고, setLoading 이라는 다소 관계없는 함수 넣었다

  // 사진 정보 db에서 가져오기
  useEffect(() => {
    getPictsFromDB();
  }, [getPictsFromDB]);

  // Intersection observer
  useEffect(() => {
    // 위의 useEffect 함수를 통해 pictureData가 불러와지면 해당 함수를 실행한다
    // 최대 이미지 숫자 개수를 넘어가지 않도록 한다
    pictureNum =
      pictureNum > PictureDatasLength ? PictureDatasLength : pictureNum;

    // setLoading(true)
    setPictures((prevPicts) => {
      return [...prevPicts, ...pictureDatas.slice(curPictNum, pictureNum)];
    });

    setHasMore(pictureDatas.slice(curPictNum, pictureNum).length > 0);
    setCurPictNum(pictureNum);
    // setLoading(false)
  }, [pictureNum, pictureDatas]);

  return { loading, pictures, hasMore };
}