Skip to content

Latest commit

 

History

History
132 lines (115 loc) · 8 KB

20211231.md

File metadata and controls

132 lines (115 loc) · 8 KB

React

React Router DOM (cont.)

6버전 업그레이드된 API 및 React Router DOM 유의사항

  • BrowserRouter 안에 Routes를 감싸는 Switch 컴포넌트 이름이 Routes로 변경되며 경로 매칭 알고리즘이 향상되었다.
  • Route 컴포넌트에서 exact prop이 지정하지 않아도 default가 되었고, 순서에 상관없이 정확한 경로를 찾아 라우팅해준다.
  • 이전에는 children, component라는 prop을 통해 경로에 따라 렌더링 할 컴포넌트를 받았었는데, 이제는 element라는 prop으로 받는다.
    • 이전에는 component prop으로 경로에 따라 렌더링할 컴포넌트를 전달하는 경우 컴포넌트 함수참조값만을 전달하면서, prop을 전달하기 어려웠다.
    • render props pattern이나 HOC으로 prop를 전달해야만 했다.
      • e.g. <Route component={Signin} /> 이렇게는 프롭전달 못하니까 아래 방법들을 사용가능 but 복잡귀찮
        1. render prop을 쓰는 방식으로 <Route render={(prop) => <Signin prop={myprop}/>} />
        1. RRD가 제공하는 HOC withRouter로 해당 컴포넌트를 감싸면, 하위 컴포넌트에 Router에서 전달받는 특정 데이터를 전달할 수 있다. forwardRef처럼 감싸서 export하면 컴포넌트를 호출하는 곳에서 전달하는 데이터를 접근할 수 있다.
    • 이젠 prop을 넣은 JSX 문법으로 쓴 컴포넌트를 전달하기만 하면 되도록 변경되면서, Route의 prop과 사용자가 전달하는 prop을 동시에 받을 수 있다. (e.g. <Route element={<Signin prop={myprop} />}>)
    • withRouter도 쓸 필요가 없어진 게, param이나 location을 useParams, useLocation 등 RRD가 제공하는 훅을 통해 접근할 수 있다. useNavigate 또한 route match를 가져오는 훅으로 제공된다.
  • Route에서 지정해주지 않은 경로에 대해서 이전 버전에서는 Redirect라는 컴포넌트로 관리했지만 이제는 Route로 바로 관리하자.
    • 6버전에서는 <Route path="*" element={<{RedirectComponent} />} />로 지정된 path 외의 모든 경로에 대해서 컴포넌트를 전달하면 처리된다.
    • 또는 <Route path="*" element={<Navigate to="{다른 Route의 path 값}" replace="true" />} />로 redirect하는 경로를 넣어주면 미리 준비된 다른 route로 안내한다.
    • 이 때 replace 값은 history에서 redirect 전의 invalid page를 빼준다. (현재의 준비된 redirect path로 대체)
    • Navigate 전의 특정 값을 담아 넘겨줄 수 있는 state prop도 있다.
// 지정되지 않은 경로 요청을 대응할 컴포넌트
function PageNotFound(props) {
  console.log(props);
  return (
    <div>
      <h2>페이지를 찾을 수 없습니다.</h2>
      <Link to="/">홈페이지</Link>로 이동
    </div>
  );
}

export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="page-not-found" element={<PageNotFound />} />
        <Route
          path="*"
          element={<Navigate to="page-not-found" replace={true} />}
        />
      </Routes>
    <BrowserRouter>
  • RRD에서는 HashRouter보다는 BrowserRouter를 쓸 것을 권장하고 있다.
    • BrowserRouter는 history API를 사용하므로 구형 브라우저에서는 지원되지 않는다는 점을 유의할 것

NavLink

  • 내가 현재 머물고 있는 경로의 NavLink에는 active라는 활성클래스가 기본으로 제공되는데, 이 활성클래스 이름을 바꿀 수 있다.
    • 원래는 isActive라는 상태를 받아 style이나 className에 함수로 ({isActive}) => isActive ? 'customActiveClassName':'' 등으로 처리해줄 수 있다.
    • 하지만 우리는 styled Component를 쓰는데, styled component가 먼저 실행되면서 이 className에 작업하기 때문에 위의 함수가 의도한 대로 실행되지 않고 함수코드가 그대로 문자열로 클래스가 되어 html에 들어간다.
    • styled component를 사용할 때 활성 클래스 이름을 커스터마이징 하려면 RRD v5만이 제공하는 activeClassName을 통해 변경하고, styled Component에서 받는 클래스네임을 처리해주는 HOC를 만들어 NavLink를 래핑해주어야만 한다.
  • useHref, useClickHandler를 사용하여 나만의 NavLink도 만들 수 있다.

Outlet

  • Layout과 같이 모든 페이지에서 중복된 컴포넌트는 outer route로 감싸서 처리해줄 수 있다.
    • 그러나 이 때 outer route component로 지정된 컴포넌트 안에 Outlet 컴포넌트를 넣어주어야 children route를 렌더링해준다.
// App.js 또는 outer Route를 렌더링 하는 곳
export default function App() {
  return (
    <Router>
      <Routes>
        <Route element={<Layout/>}>
            <Route path="/" element={<Home />} />
            <Route path="signin" element={<SignIn />} />
            // ... 이런 식으로 다른 Layout 속에 들어갈 자식요소들은 Outlet으로 끼워줘야만 렌더링된다.
        </Route>
      </Routes>
    </Router>
  );
}


// Layout.js before 
export function Layout({children}) {
  return (
    <Container>
      <Header />
      <Wrapper as="main">
        {children}
      </Wrapper>
    </Container>
  );
}

// Layout.js after
import { Outlet } from 'react-router-dom';

export function Layout() {
  return (
    <Container>
      <Header />
      <Wrapper as="main">
        <Outlet />
      </Wrapper>
    </Container>
  );
}

React Form

  • React가 제어하는 controlled component로 form 요소들을 만들어주어야 dom script가 아닌 setState을 통한 form 요소 제어가 가능하다.

Controlled input component

  • input의 value 등 사용자 입력으로 인해 바뀌는 요소에는 state를 prop 값으로 주고(initial value를 빈문자열 등으로 주면 된다), onChange 이벤트에 setter함수를 걸어주어야 Controlled component로서 form 요소를 사용할 수 있다.
    • onChange에 핸들러를 등록하지 않고 value만 주면, readOnly가 되기 때문에 ReadOnly를 명시하거나 이벤트핸들러를 등록해주어야만 한다.
  • onChange event는 원래대로라면 input 요소에서 focus가 사라지는 blur에 발생하지만 react에서는 input 이벤트도 change로 감지된다.

Controlled button component

  • form 요소 안에 있는 button 또는 엔터 키로 submit하려면, form 요소에 주는 onSubmit 핸들러에서 e.preventDefault로 기본동작을 취소해주어야 한다.
  • reset 버튼을 만드려면 button type을 reset으로 설정한 후, setter함수로 input의 value들에 빈 문자열을 넘긴다.

Controlled TextArea component

  • input 요소와 동일하게 value를 받아 처리한다.

Controlled Select component

  • 직접 만들기는 어려우니 오픈소스의 Formik을 사용하자.
    • redux form이라는 패키지도 있지만 HOC라서 사용하기가 복잡하다.
  • html요소와 달리 select 요소의 value로, change event를 setter함수로 넣어준다.
  • 멀티select로 처리하려면 배열을 사용하여 e.target의 children을 가져와 처리한다.

Uncontrolled component

  • input type 을 file로 갖는 등 특수한 경우에는 어쩔 수 없이 uncontrolled component를 사용해야만 한다.
  • value, onChange를 갖지 않는 것을 uncontrolled component라고 하며, useEffect, useRef 등을 통한 dom script로 제어해야한다.

React Helmet

  • head 태그에 들어갈 title이나 meta 태그들, open graph protocol을 위한 정보들 등은 react component가 body의 root에만 렌더링되는데 어떻게 넣어줄 수 있을까?
    • 위의 상황 + React에서 SEO 준수를 위한 처리를 해주려면 다른 방법이 필요할 것이다.
  • react-helmet-async을 통해 Head 태그에 들어갈 것들을 넣어줄 수 있다. (현재 react-helmet은 더이상 업데이트되지 않으니까 꼭 react-helmet-async로 설치해주어야만 한다.)
  • `$ npm i react-helmet-async'으로 설치한 후 HelmetProvider를 import하여 App을 감싸고, head태그에 넣을 각 component에서 Helmet을 import하여 넣어준다.