ContextProvider를 사용할때 Route에는 어떻게 적용하는게 효율적일지!

<Route path="/app/dashboard">
  <DashboardContext>
    <Dashboard></Dashboard>
  </DashboardContext>
</Route>

반복적으로 위와같이 입력하기 어려우니 ContextRoute를 생성하자


import React from 'react';
import { Route } from 'react-router-dom';

const ContextRoute = ({ provider, component, ...rest }) => {
  const { Provider } = provider;
  const Component = component;

  return (
    <Route {...rest}>
      <Provider>
        <Component />
      </Provider>
    </Route>
  );
};

export default ContextRoute; 

위에 ContextRoute를 정의한 이후에는 다음과같이 작성하면 된다.

<ContextRoute
  path="/app/dashboard"
  provider={DashboardContext}
component={Dashboard}
/>

참고

React Context는 오랫동안 사용이 가능했지만 이번에 React hooks이 나오면서 더 좋아졌다. 이전에는 redux라는 third-party libraries를 사용해서 React Apps의 상태를 관리했었다. 이 개발자가 redux에 대한 내용 을 작성했는데, redux를 사용할 개발자들은 이 해당 포스트를 참고해도 좋을것 같다. (참고로 이 코드를 React context hook 으로 리팩토링을 진행했음

React Redux를 사용할때

나는 react-context-hook을 사용하고 싶으니... 이 코드의 흐름을 이해해보려고 한다. 이전 redux를 사용할때는 component, container, ilb/api, modules를 각각 생성했었는데, 코드 관리가 어려웠다. 하나의 API를 추가하려면 너무 많은 작업이 필요했고, 여러 파일을 변경하면서 수정하는게 보통 쉬운게 아니다. 내가 모듈에 작성했던건지.. lib/api에 작성했던건지 나도 기억이 안나니.. 보통 Redux를 사용하면 Component에 내가 원하는 UI를 표시하고, Container안에 component를 넣고 container에서는 데이터를 lib/api, modules을 통해 가져온다. 역할로 나눠보면 lib/api, modules은 데이터 처리를 해주고, container에서는 데이터 처리의 상태를 확인하다가 변경된 부분이 있다면 데이터를 업데이트를하고, component에서는 container에서 업데이트된 데이터를 화면에 출력을 한다.

위 작업을 하기 위해서는 첫번째로 component를 작성하고, 두번째로 lib/api, module을 작성을 한뒤에 컴포넌트에 데이터가 정상적으로 들어가는지 확인하고, 이후에는 변경되는 데이터를 확인하고 업데이트하는 container를 작성해 최종적으로 component가 아닌 container를 pages에 추가했었다. 이방법이 정답은 아니겠지만 내가 본 책에서는 그랬다. 지금 다른 코드를 보면 이 책도 예전 방식을 따라했던것 같다. 너무 빠르게 변화함..

React Context를 사용하면

React Context를 사용하면 context, container, component를 생성하면된다.

  • Context
    • reducer를 구현해 어떤 액션 타입이 왔을때 어떻게 데이터를 리턴할지에 대해서 명시
    • 추가로 article에 대한 articleContext.js를 생성하고, 그 안에는 ArticleProvider를 정의한다. 이 Provider는 값을 전달하는 역할을 한다.
  • Container
    • container는 context를 이용해 article의 내용을 가져오는 역할을 한다. useContext(ArticleContext를 통해 articles을 가져오고 article을 화면에 출력할 내용을 생성한다. (출력할때 component에 미리 Article 을 만들어놓으면 여기서 사용)
  • Component
    • 위 container에서 작업할 Article을 만든다.
    • ArticleAdd.js를 생성하는데 여기서도 useContext(ArticleContext)을 이용해 dispatch를 가져온다.
      • newAddArticle과 같은 함수를 호출한다.
      • 호출되는 함수에서는 dispatch를 통해 새로운 Article이 추가되었다고 알린다.
  • App.js
    • 최종 앱에는 아래 같은 하위 구성을 정의한다.
      • ArticleProvider (context)
        • AddArticle (component)
        • Articles (container)

참고

DataFrameSuiteBase

  • DataFrameSuiteBase를 이용해서 주어진 두개의 DataFrame이 일치하는지 확인이 가능하다.
  • 간단하게 사용방법은 두개의 결과 df1, df2를 생성하고 assertDataFrameEquals를 사용하면 된다.
  • 사용하면서 까다로웠던것은 DataFrameSchema도 확인을 한다. 심지어 Nullable을 체크...
  • 만약 조인했던 결과에 null이 들어있고, 기대하는 값에 null이 없었다면 두개의 Schema는 다르고 결과적으로 Fail이 난다.
  • expectedDf에 만약 Null의 값을 포함시키기 위해서는 다음 Null값이 포함되는 DataFrame 생성하기 포스트 를 확인
  • 참고로 아이템의 순서도 동일해야 한다! 아이템 정렬하는 방법은 아래 DataFrame 정렬하기 포스트를 확인

참고

  • 기존에 /src/components에서 각각 component를 생성했다면
  • component 생성된 녀석들을 가져와서 사용을 해보자
yarn add @material-ui/core
  • material-ui/core를 설치
  • components에 추가하면 아래와 같은 에러가 난다.
index.js:1 Warning: Failed prop type: The prop `children` is marked as required in `ForwardRef(Button)`, but its value is `undefined`.
    in ForwardRef(Button) (created by WithStyles(ForwardRef(Button)))
    in WithStyles(ForwardRef(Button)) (at Search.js:18)
    in div (created by styled.div)
    in styled.div (at Search.js:16)
    in Search (at AptComplexPage.js:12)
    in div (at AptComplexPage.js:9)
    in AptComplexPage (created by Context.Consumer)
    in Route (at App.js:19)
    in App (at src/index.js:41)
    in HelmetProvider (at src/index.js:40)
    in Router (created by BrowserRouter)
    in BrowserRouter (at src/index.js:39)
    in Provider (at src/index.js:38)
  • 어이없게도 <Button> </Button> 사이에 값을 넣지 않았다... 값을 넣지 않으면 children undefined 에러가 생긴다

스타일 적용하기

import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles(theme => ({
  root: {
    display: 'flex',
    flexWrap: 'wrap',
  },
  textField: {
    marginLeft: theme.spacing(1),
    marginRight: theme.spacing(1),
    width: 200,
  },
}));


(...)
   const classes = useStyles();

  return (
    <div className={classes.root}>
      <div>
        <TextField
          id="standard-full-width"
          label="Label"
          style={{ margin: 8 }}
          placeholder="Placeholder"
          helperText="Full width!"
          fullWidth
          margin="normal"
          InputLabelProps={{
            shrink: true,
          }}
        />
  • useStyles 각각의 tag별로 정의하고 root의 경우 <div>에 지정
  • 나머지 textfield, button등에 스타일을 주면 적용이 된다.

Parameter, Query Example

  • /user/lee
  • /search?keyword="hi

위에서 user에 lee를 넘기는 방식이 parameter로 특정 사용자를 조회할때 사용하고, query는 키워드를 검색할때 페이지에 필요한 옵션을 전달할 때 사용을 하게 된다.

URL Parameter

  • match라는 객체안의 params 값을 참조
import React from 'react';

const data = {
  seoul: {
    name: '서울',
    location: 'seoul'
  },
  gyonggi: {
    name: '경기도',
    location: 'gyonngi-do'
  }
};

const Location = ({ match }) => {
  const { cityname } = match.params;
  const city = data[cityname];
  if (!city) {
    return (
      <div>
        <h1> {cityname} 은 존재하지 않는다.</h1>
      </div>
    );
  }

  return (
    <div>
      <h1>
        {' '}
        {cityname} ({city.name})
      </h1>
      <p>{city.location}</p>
    </div>
  );
};

export default Location;
  • App.js에서 routing을 정의
import React from 'react';
import { Route, Link } from 'react-router-dom';
import './App.css';

import Home from './components/Home';
import Search from './components/Search';
import Location from './components/Location';

const App = () => {
  return (
    <div>
      <Link to="/">홈</Link>
      <Link to="/search">검색</Link>
      <Link to="/location/seoul">서울</Link>
      <Link to="/location/gyonngi">경기</Link>
      <Link to="/location/star">별나라</Link>

      <Route path="/" component={Home} exact={true}></Route>
      <Route path="/search" component={Search}></Route>
      <Route path="/location/:cityname" component={Location}></Route>
    </div>
  );
};

export default App;

URL 쿼리

  • url 쿼리의 경우에는 match와 다르게 location의 객체에 있는 값을 조회가 가능하다.
  • /search?keyword=hi의 url을 호출했을때 location의 객체의 형태는 아래와 같다.
{
  "pathname": "/search",
  "search": "?keyword=hi"
}
  • query 문자열을 객체로 변환할때는 qs의 라이브러리를 사용한다
yarn add qs
import React from 'react';
import qs from 'qs';

const Search = ({ location }) => {
  // ?keyword=
  const query = qs.parse(location.search, {
    ignoreQueryPrefix: true // prefix: ?
  });

  const keyword = query.keyword;

  return (
    <div>
      <h1>Search</h1>
      <p> 검색 쿼리는 {keyword}</p>
    </div>
  );
};

export default Search;
  • 다음과 같이 추가한 이후에 url을 http://localhost:3000/search?keyword=hi을 통해 접속하면 원하는 결과를 얻을 수 있다.

+ Recent posts