리액트 기본 과정 과제 - 신조어 사전

박경준·2021년 6월 30일
0

main course - react

목록 보기
6/6

신조어 사전!

firestore의 데이터 정렬하기

// dict.js
// Action Creators
export const loadDictFB = () => {
  return function (dispatch) {
    dict_db.get().then((docs) => {
      let dict_data = [];
      docs.forEach((doc) => {
        if (doc.exists) {
          dict_data = [...dict_data, {id: doc.id, created_at: doc._delegate._document.version.timestamp.seconds, ...doc.data()}]
          // 파이어스토어의 특성 상 data id값에 의해 정렬되는데 나는 등록된 시간 순서에 따라 정렬하고 싶었음.
          dict_data.sort(function(a, b)  {
            return a.created_at - b.created_at;
          });
          // data를 리덕스 스토어에 넣고 created_at에 의해 정렬될 수 있게 한다.
        }
      })
      dispatch(loadDict(dict_data));
    })
  }
};

create 직후 root 페이지로 돌아가면서 최하단으로 스크롤하기

// List.js
...
const scrollTarget = useRef();
...
if (shouldScroll && scrollTarget.current) { 
  // scrollTarget.current 를 두번(리덕스 데이터 불러올때, 파이어스토어 데이터 불러올때) 불러오는데
  // 리덕스 데이터때에는 scrollTarget.current 값이 없으므로 scrollTarget.current가 있을때만 scroll되도록 한다.
  scrollTarget.current.scrollIntoView({behavior: 'smooth', block: 'end'})
}
<ListWrap ref={scrollTarget}></ListWrap>
// App.js의 Container 태그가 아니라 List.js의 ListWrap 태그에 ref를 걸어줘야 스크롤이 동작한다...

Delete reducer에서의 return

//
case "dict/DELETE": {
  const new_dict_list = state.list.filter((l, idx) => {
    return action.id !== l.id
  })
  return {...state, list: new_dict_list}
  // 여기서 list의 value로는 spread 할당을 해주면 안됨. return {...state, list: [...state.list, ...new_dict_list]} 이런 식으로...
  // 기존 list와 new_dict_list의 차이는 지워야할 객체가 있냐없냐인데
  // ...state.list를 해버리면 지워야할 객체를 함께 담아버리는꼴임.
}

무한 스크롤 적용하기

무한스크롤 동작에 관여된 파일은 FetchMore.js와 List.js이다.

  • FetchMore.js
    div 태그를 만들어서 ref로 설정하고, IntersectionObserver를 이용해서 해당 div 태그가 화면에 들어오면 즉, 화면 안에 최하단 영역이 들어오면 무언가 동작을 취해준다.
    여기서 동작은 page 변수의 값에 +5를 해줌으로써 현재 보이고 있는 리스트에서 5개를 더 보여주게 된다.
// FetchMore.js
import React, { useRef, useEffect } from "react";
import "./FetchMore.css";

const FetchMore = ({ loading, setPage }) => {
  const fetchMoreTrigger = useRef(null);
  const fetchMoreObserver = new IntersectionObserver(([{ isIntersecting }]) => {
    if (isIntersecting) setPage((page) => page + 5);
  });

  useEffect(() => {
    const fetchMoreTrigger_current = fetchMoreTrigger.current;
    fetchMoreObserver.observe(fetchMoreTrigger.current);
    return () => {
      fetchMoreObserver.unobserve(fetchMoreTrigger_current);
      // 컴포넌트가 사라졌을때, observe를 떼어주는 동작인데, /write 페이지로 넘어가면 에러가 난다.
      // 그냥 fetchMoreTrigger.current를 return에 사용하면 값이 바뀔 수 있다는 경고문구가 떴다.
      // 그래서 useEffect 안에서 변수로 한번 할당해두고 그 변수를 적용하면 해결!
    };
  }, []);

  return (
    <div
      id="fetchMore"
      className={loading ? "loading" : ""}
      ref={fetchMoreTrigger}
    />
  );
};

export default FetchMore;
  • List.js
    여기서 중요한 점은 page state를 부모 컴포넌트에서 생성해주고 page의 값을 변경하는 setPage를 자식 컴포넌트에 props로 보내서 자식 컴포넌트에서 setPage를 이용해 page 값을 변경시키면 부모 컴포넌트의 page가 변경된다...! 그 변경된 page의 값이 변화되면 콜백함수에서 다음 다섯개의 데이터를 붙여준다.
// List.js
...
import FetchMore from "./FetchMore";
...
  const [page, setPage] = useState(0);
  const [loading, setLoading] = useState(false);

  useEffect(async () => {
    setLoading(true);
    dispatch(loadDictFB(page));
    // page 변수가 변경되었음을 감지하고 콜백함수를 실행시킨다.
    // 여기서는 loadDictFB(page)를 실행하여 원하는 구간의 데이터를 추가시킨다.
    setLoading(false);
  }, [page]);
...
  <FetchMore loading={page !== 0 && loading} setPage={setPage} />
...

무한스크롤 로딩 방식으로 인해 변경된 기존 코드들

최초 로딩 시엔 리스트에 아무것도 없으므로, 저절로 FetchMore를 감지하고 page에 +5가 됨. 따라서 마운트 시 loadDictFB는 안써도 됨.

// App.js
const mapDispatchToProps = (dispatch) => ({
  load: () => {
    // dispatch(loadDictFB());
  },
});
...
  componentDidMount() {
    // this.props.load(); // 컴포넌트 최초 렌더링 시 db 데이터를 받아옴
  }

미처 해결 못한 이슈...

  1. 메인페이지에서 /write로 갔다가 뒤로가기로 메인페이지를 가면 렌더링에 문제가 생김 (기존 리덕스에 들어가있는 list와 새롭게 불러오는 list간에 겹치는 부분이 생김 -> 뷰 문제 및 key 중복 에러)

    심화과정에서 해결할수 있다고하니 일단은 새로고침을 넣는다...

//Write.js
...
useEffect(() => {
  if (props.match.path === "/write") {
    dispatch(reload(true));
  }
  return;
}, []);
...
if (props.isEdit) {
...     
} else {
...
  dispatch(reload(false)); // 수정하기 기능에서는 roload가 true가 되면 쓸데없이 새로고침 되므로.
}
useEffect(() => {
  if (reload) {
    window.location.reload(); // /write 페이지로 갔다가 다시 돌아왔을때 렌더링에 문제가 있어서 임시로 새로고침 처리함.
    dispatch(reload(false));
  }
}, []);
  1. 무한스크롤로 데이터를 불러올때, 파이어베이스 메서드를 이용해서 원하는 크기만큼만 데이터를 불러왔어야 한다... 그것이 무한스크롤의 의의...

야호~!

profile
빠굥

0개의 댓글