리액트-route transition animation (2)
전편을 안보고 오신분들은 전편을 보고오시라.
우선 이글을 통해 얻어갈 수 있는 것들을 알려주겠다.
- 전역 history 객체를 이용해 history.listen 이벤트를 설정하는 방법
- 이벤트를 통한 redux에 pathname 을 저장해 , 이전 패스와 비교하는 방법
- 저장된 pathname을 통해 바뀐값을 비교하여, 애니메이션 방향을 결정하는 방법
- TransitonGroup 의 childFactory 속성을 이용해서 classNames 속성을 바꿔주는 방법
- history.state를 이용해 좀더 쉽게 애니메이션 넣는 방법
뒤로가기 눌렀을땐, 어떻게 해요?
문제가 있다. 구현하고자하는 목표는 앞으로넘길땐 오른쪽에서, 뒤로가기했을땐 왼쪽에서 페이지가 넘어가는 방식으로 구현을 해야한다.
한방향으로만 할거면 이전상태의 정보가 필요없지만, 양방향으로 움직일려면 결국 이전상태의 정보가 필요하다.
이전 상태의 정보란 이전페이지의 url 정보를 의미하고 브라우저에서는 해당정보를 history 라는것으로 관리를한다.
위글에서 브라우저 history와 react router dom V6에서 히스토리 정보를 react element 바깥에서 접근할수 있는 방법을 기술해 놓았다.
위와같은 방법으로 history를 밖에서 커스텀하여서 관리를 하게되면 장점이 하나 생기는데 바로 history.listen 과 같이 이벤트를 등록시킬 수 있다는 점이다.
전역 history 객체를 이용해 history.listen 이벤트를 설정하기
import { createBrowserHistory } from 'history';
import { routeChange } from './state/actions-creators';
import { store } from './state/storeSetting';
const history = createBrowserHistory();
history.listen(({ action, location }) => {
store.dispatch(routeChange({ pathname: location.pathname }));
});
export default history;
history.js
위처럼 history.js 파일을 만들어서 싱글톤으로 export를 해주고, router 등록할 때 history 객체를 주입시켜주면 history의 변경 이벤트 ( url변경) 가 발생할때마다 이벤트를 받아 볼 수가있다.
history api 의 설명부분의 listen 부분
또한 redux의 state에 액션을 디스패치할때도 react element 밖에서 액션을 디스패치 할 수있는데 이를 통해서
이전 상태의 정보를 관리할 수있게된다.
물론 react-router을 쓰게되면 context api 처럼 history의 상태를 알아서 관리를 해주지만 우리는 좀더 history정보를 커스텀하게 꺼내와서 관리를 하고 싶어서 이런 구성을 하는것이다.
이벤트를 통한 redux에 pathname 을 저장해 , 이전 패스와 비교하기
import { ROUTE_CHANGE } from '../action-types';
export const routeChange =
({ pathname }) =>
async dispatch => {
// console.log(pathname);
dispatch({ type: ROUTE_CHANGE, payload: pathname });
};
state/action-creators/routeChange.js
간단하다 위 history.js 에서 listen이벤트가 발생하면 ROUTE_CHANGE 라는 액션을 발생시키는 코드이다. pathname을 인자로 받는다.
import { ROUTE_CHANGE } from '../action-types';
const INITIAL_STATE = {
currentPage: '',
slideFromDirection: ''
};
const pageOrder = [
// '/', // 메인 페이지
'/ticketing/landing', // 티켓예매 처음일때 ( 인증안되었을 때 들어가는 페이지)
'/list/landing', // 리스트 랜딩용 페이지
'/auth/message', // 인증용 메세지 보내는 페이지
'/auth/validation', // 인증 시키는 페이지
'/ticketing/amount', // 티켓 수량 입력
'/ticketing/deposit', // 티켓 입금자명, 입금주소 모달창 띄우는 페이지
'/list/mytickets', // 내 티켓 리스트, 패스이름 바꾸고 싶음 바꿔도됨, 밑에 processForValidationNextPage 도 바꿔주삼 - 2월 1일 11:52 이찬진
];
// eslint-disable-next-line import/no-anonymous-default-export
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case ROUTE_CHANGE:
let pathname = action.payload;
// 슬라이드 방향 디력선 설정
let slideFromDirection = 'right';
if (pageOrder.indexOf(state.currentPage) > pageOrder.indexOf(pathname)) {
slideFromDirection = 'left';
}
// 기본
return {
...state,
currentPage: pathname,
slideFromDirection: slideFromDirection,
location: action.payload.location
};
default:
return state;
}
}
state/reducers/routePagination.js
이 코드말고도 , 티켓예매냐, 내티켓확인 이냐 에따른 프로세스에 관한 다음에 보여줄 화면을 결정하는 코드도 있지만 그부분은 제외시키고 슬라이드 방향에 관한 설정 코드를 기술하였다. 간단하다 순서를 정하고싶은 pageOrder 를 리스트로 선언해서
해당 pathname과 이전 state에서 가지고있었던 pathname 정보를 비교를 해서 슬라이드 방향이 왼쪽이냐 오른쪽이냐를 결정을 한다.
슬라이드 방향을 결정한후 , 리덕스에서 슬라이드 방향정보에따른 애니메이션 방향 결정하기
지금까지 history 객체를 커스텀하여설정해서 redux에서 페이지에 관한 정보를 등록함과동시에 , 순서에따른 slideFromDirection 정보를 저장해서 , 좌우 슬라이딩 방향을 결정 하였다. 이제 그려주기만 하면된다.
import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { Routes, Route, useLocation } from 'react-router-dom';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import MessageValidationProcess from '../MessageValidationProcess/MessageValidationProcess';
import TicketingProcess from '../TicketingProcess/TicketingProcess';
import './Pagination.css';
import ListProcess from '../ListProcess/ListProcess';
import TicketCodePage from '../ListProcess/TicketCodePage/TicketCodePage';
function Pagination() {
const location = useLocation();
const slideFromDirection = useSelector(
state => state.routePagination.slideFromDirection
);
return (
<TransitionGroup
className="transitions-wrapper"
childFactory={child => {
return React.cloneElement(child, {
classNames: slideFromDirection
});
}}
>
<CSSTransition
key={location.pathname}
classNames={slideFromDirection}
timeout={1000}
>
<Routes location={location}>
<Route
path="/ticketing/*"
element={<TicketingProcess location={location} />}
/>
<Route path="/list/*" element={<ListProcess location={location} />} />
<Route
path="/auth/*"
element={<MessageValidationProcess location={location} />}
/>
</Routes>
</CSSTransition>
</TransitionGroup>
);
}
export default Pagination;
components/Pagination/Pagination.js
고스락 프로젝트에서 메인페이지를 제외한 애니메이션이 필요한 부분들을 Process 컴포넌트 형태로 제공하는데 , TicketingProcess 파일을가면 또 Routes가 존재한다. 일정부분을 잘 감싸서 애니메이션을 제공하는 context api 같은 형식이라고 생각하시면 된다.
useSelector를 활용해서 redux에 저장되었던 슬라이딩 정보를 꺼내와준후에, CSSTransiton 컴포넌트에 classNames를 지정해준다.
.transitions-wrapper {
position: relative;
overflow: hidden;
height: calc(var(--vh, 1vh) * 100);
width: 100vw;
}
.right-enter {
/* z-index: 0; */
transform: translateX(100%);
}
.right-enter-active {
/* z-index: 1; */
transform: translateX(0);
transition: transform 300ms ease-in-out;
/* transition-timing-function: ease-in-out; */
}
.right-exit {
/* z-index: 1; */
transform: translateX(0);
}
.right-exit-active {
/* z-index: 0; */
transform: translateX(-100%);
transition: transform 300ms ease-in-out;
/* transition-timing-function: ease-in-out; */
}
.left-enter {
/* z-index: 0; */
transform: translateX(-100%);
}
.left-enter-active {
/* z-index: 1; */
transform: translateX(0);
transition: transform 300ms ease-in-out;
/* transition-timing-function: ease-in-out; */
}
.left-exit {
/* z-index: 1; */
transform: translateX(0);
}
.left-exit-active {
/* z-index: 0; */
transform: translateX(100%);
transition: transform 300ms ease-in-out;
/* transition-timing-function: ease-in-out; */
}
components/Pagination/Pagination.css
slideFromDirection 정보에는 left , right 두가지 정보가 들어가있고 위 css 파일과같이 왼쪽으로 넘어오는거면
enter , enter-active , exit , exit-active 앞에 left를 쓸지 right를 쓸지 결정해준다고 보면된다.
주의할점은
<TransitionGroup
className="transitions-wrapper"
childFactory={child => {
return React.cloneElement(child, {
classNames: slideFromDirection
});
}}
>
TransitonGroup의 childeFactory 부분인데, 이부분이 없으면 slideFromDirection 정보가 바뀐뒤에도 계속 방향이 일정하게 남아있게된다. 그러면초기 한방향으로만 고정되어 움직이게된다. 따라서 위부분을 꼭추가해서 슬라이드 방향정보가 바뀌면 슬라이드 방향도 바뀔수 있게 세팅하도록하자.
별첨 : history.state를 이용해 좀더 쉽게 애니메이션 넣는 방법
본인의 프로젝트에서 애니메이션이 들어갈부분이 얼마 없다면, 지금까지 기술한 redux에 정보를 저장해서 공통으로 관리하는 느낌이아닌
history.state를 이용해서 앞으로 갈지 뒤로갈지 방향정보를 줄 수가 있다.
어떤 한 컴포넌트에서 버튼을 눌렀을때 ( history 는 지금 전역객체로 커스텀해서 가져온거다. 만약 커스텀하지 않으셨다면
react-router-dom v6 기준 useNavigate를 쓰시면된다. history를 커스텀하셔도 밑에 navigate 형식으로 하셔도 동작한다.)
history.push(
'/tickets/check',
{ slideDirectionFrom : 'right' }
);
//or
const navigate = useNavigate();
navigate('/tickets/check', {
state: { slideDirectionFrom : 'right' }
});
위와같은 방식으로 state를 넘기게되면, 해당 라우터에 속해있는 컴포넌트들에서
import { useLocation } from 'react-router-dom';
import history from '../../../history';
//컴포넌트안
const location = useLocation();
useEffect(() => {
// 커스텀 히스토리를 이용했을 때
console.log('history.location.state:', history.location.state);
//result: '{ slideDirectionFrom : "right" }'
// 기본제공 로케이션을 이용했을 때
console.log('location:', location);
//result: '{pathname: '/tickets/check', search: '', hash: '', state: {…}, key: 'xf82gqmb'}'
}, [location]);
이런식으로 받아볼수있다. 그럼뭐 간단하게 리덕스를 이용안해도 방향을 쉽게 결정할수 있을것이다.
위 프로젝트는 고스락 티켓예매 프로젝트 21th 에 관련한 내용입니다.
소스 전부는 아래에서 보실수 있습니다!
오랜만에 블로그...복귀를 했다
디프만 11기로 활동하고 , 티키타가 앱만들고 , 고스락 22th 프로젝트 현재진행중이다.!
좀더 열심히 써야지...!