리액트 기본 과정 과제 - 신조어 사전
신조어 사전!
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 데이터를 받아옴
}
미처 해결 못한 이슈...
- 메인페이지에서 /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));
}
}, []);
- 무한스크롤로 데이터를 불러올때, 파이어베이스 메서드를 이용해서 원하는 크기만큼만 데이터를 불러왔어야 한다... 그것이 무한스크롤의 의의...
야호~!
Author And Source
이 문제에 관하여(리액트 기본 과정 과제 - 신조어 사전), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@uvula6921/리액트-기본-과정-과제-신조어-사전저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)