Skip to content

redux로 메인 피드의 data 를 관리해보자.

jominji edited this page Dec 13, 2019 · 2 revisions

데이터를 redux로 글로벌하게 기억하는 하는 메인 피드 infinite scroll을 만들어보자.

FeedWorkContainer 설계

  • data는 redux에서 global하게 관리되는 배열이다.
  • useGetFeedWallpaperList는 커스텀 훅이다. skippedNum은 state로 관리되는 변수이다. fixedNum은 useRef로 관리되는 변수이다. 9라는 값을 가지고 있다. (ex,몽고디비에서 0개를 skip하고 9개를 가져온다, 몽고디비에서 9개를 skip하고 9개를 가져온다 등등 )
  • 맨 처음 render 되면 scroll이벤트를 달아준다.
  • skippedNum이 변경되면 doFetch() 함수를 실행한다. doFetch()는 url state를 변경시키는 setUrl() 함수이다.
  • data와 isLoading을 FeedWorks 컴포넌트에 넘겨준다.
const FeedWorkContainer:React.FC = () => {
  const data = useSelector((state: RootState) => state.feed.workData);
  const [{
    isLoading, isError, skippedNum, fixedNum,
  }, doFetch, onInsert] = useGetFeedWorkList();

  useEffect(() => {
    window.addEventListener('scroll', onInsert);
    return () => {
      window.removeEventListener('scroll', onInsert);
    };
  }, [skippedNum]);

  useEffect(() => {
    doFetch(`${API_SERVER}/feed/images/more/${fixedNum.current}/${skippedNum}`);
  }, [skippedNum]);

  return (
    <FeedWorks
      data={data}
      isLoading={isLoading}
    />
  );
};

useGetFeedWorkList 설계

  • url은 API서버에 요청할 주소를 관리하는 state이다.
  • skippedNum이 업데이트되면 url이 업데이트되고 url이 변경되면 useEffect가 트리거된다. API 서버에서 데이터를 가지고온다.
  • 가져온 데이터를 이용해 redux global의 data를 업데이트한다.
  • 그럼, skippedNum은 언제 업데이트되는것일까?
    • 사용자가 스크롤을 하면 onInsert() 이벤트 핸들러가 호출된다.
    • 이 이벤트 핸들러는 사용자의 위치가 맨 끝인지 아닌지 확인한다.
    • 사용자의 위치가 맨 끝이라면 skippedNum 에 + 9를해 업데이트한다.
  • 왜 skippedNumG를 redux global로 관리했을까?
    • 사용자가 스크롤을 열심히 해서 27개의 데이터가 렌더되었다고 하자.
    • 사용자가 아이템을 클릭해 상세 페이지를 이동하고 다시 뒤로가기 버튼을 눌렀다면?
    • data 가 만약 global하게 관리되지 않았다면 unmount 되면서 data가 사라지고 27개가 아닌 9개 초기 데이터가 보이게된다.
    • data 를 global하게 관리되기 때문에 그대고 27개의 데이터를 볼 수 있고
    • skippedNumG 또한 global로 관리되기 때문에 맨 밑으로 스크롤을 하게된다면 27에서 +9 가 된 36으로 업데이트된다.
const useFetch = ()
:[{isLoading:boolean, isError:boolean, fixedNum:React.MutableRefObject<number>, skippedNum:number}, (url:string)=>void, ()=>void] => {
  const [url, setUrl] = useState('');
	...
  const fixedNum = useRef(9);
  const dispatch = useDispatch();
  const skippedNumG = useSelector((state: RootState) => state.feed.workSkippedNum);
  const [skippedNum, setSkippedNum] = useState(skippedNumG);
  const isLoading2 = useRef(false);

  const onInsert = () => {
    ...
    if ((scrollTop + clientHeight) === scrollHeight) {
      if (isLoading2.current === false && skippedNumG >= skippedNum) {
        setSkippedNum(skippedNum + 9);
      }
    }
  };

  useEffect(() => {
    const fetchData = async () => {
      ...
      isLoading2.current = true;
      try {
        const result = await axios(url);
       	...
          const images = result.data.data;
          dispatch(getWorkDataMore(images));
        	isLoading2.current = false;
          ...
        ...
    fetchData();
  }, [url]);

  return [{
    isLoading, isError, fixedNum, skippedNum,
  }, setUrl, onInsert];
};

FeedWorks 설계

  • data와 isLoading을 props 로 넘겨받아 rendering 한다.
const FeedWorks:React.FC<FeedWorksProps> = ({
  data, isLoading,
}) => (
  <S.Container>
    <S.FeedWrapper>
      {
          data.map(({
            _id, ownerId, url, creator, title, numOfComments, views,
          }) => (
            <WorksCard
              ......

Redux 설계

Action

  • more파라미터는 추가로 요청한 (axios) 데이터를 받는다.
  • action에서는 payload라는 key값으로 more에 접근할 수 있다.
import { IImage } from './types';

export const WORK_DATA_MORE = 'upload/WORK_DATA_MORE' as const;

export const getWorkDataMore = (more: IImage[]) => ({
  type: WORK_DATA_MORE,
  payload: more,
});

Reducer

  • 이전의 상태값에 새로운 상태값을 덮어 씌운다.
...
const initialState: FeedState = {
  workData: [],
  workSkippedNum: 0,
};

function feed(state:FeedState = initialState, action:FeedAction) {
  switch (action.type) {
    case WORK_DATA_MORE:
      return {
        ...state,
        workData: [...state.workData, ...action.payload],
        workSkippedNum: state.workData.length + action.payload.length,
      };
    default:
      return state;
  }
}

1. 그라운드 룰

2. 스크럼 hackmd link

3. 변경 내역

4. 스프린트

5. 기술공유

6. 팀 회고록

Clone this wiki locally