[fastcampus] Ch7. Hooks (3)

productuidev·2022년 3월 15일
0

React Study

목록 보기
16/52
post-thumbnail

패스트캠퍼스 한 번에 끝내는 프론트엔드 개발(Online) 강의 정리


4. React Router Hooks

react-router-dom 에서 제공하는 router 관련 hooks 사용해 react-router를 처리하기
useHistory useParams

react-router-dom

withRouter(HOC)로 감싸서 props로 받아 history에 접근

src/components/LoginButton.jsx

import { withRouter } from "react-router-dom";

export default withRouter(function LoginButton(props) {
  function Login() {
    setTimeout(() => {
      props.history.push("/");
    }, 1000);
  }
  return <button onClick={Login}>로그인 하기</button>;
});

useHistory

props를 받아오지 않고 곧바로 useHistory를 통해서 해당 페이지의 history에 접근 가능

src/components/LoginButton.jsx

export default function LoginButton() { // props 제거

  const history = useHistory(); // 현재의 history

  function Login() {
    setTimeout(() => {
      history.push("/");
    }, 1000);
  }
  return <button onClick={Login}>로그인 하기</button>;
}

기존 params 접근 방식 (id)

dynamic router를 사용하는 경우, route에서 props를 받아서 해당 페이지에서
props를 통해서 route 주소에 적힌 params 부분으로 id에 접근 가능했음

src/pages/Profile.jsx

export default function Profile(props) {
  const id = props.match.params.id;
  return (
    <div>
      <h2>Profile 페이지 입니다.</h2>
      {id && <p>id 는 {id} 입니다.</p>}
    </div>
  );
}

useParams

useParams를 사용함으로서 props를 받아올 필요 없이 바로 parmas에 접근 가능

src/pages/Profile.jsx

import { useParams } from "react-router";

export default function Profile() { // props 제거
  const params = useParams();
  const id = params.id;

  console.log(id, typeof id); // 1, string

  return(
    <div>
      <h2>Profile 페이지입니다.</h2>
      {id && <p>id 는 {id} 입니다.</p>}
    </div>
  );
}

5. 컴포넌트 간 통신

여러 개의 컴포넌트 사이에서 데이터를 주고 받아 렌더링되는지

npx create-react-app component-communication

하위 컴포넌트를 변경하기

A의 Button을 클릭하여 E를 변경하려면?

  1. <A /> 컴포넌트에서 button에 onClick 이벤트를 만들고,
  2. button을 클릭하면, <A />의 state를 변경하여, <B />로 내려주는 props를 변경
  3. <B />의 props가 변경되면, <C />의 props에 전달
  4. <C />의 props가 변경되면, <D />의 props로 전달
  5. <D />의 props가 변경되면, <E />의 props로 전달

A컴포넌트(버튼) -> B컴포넌트 -> C컴포넌트 -> D컴포넌트 -> E컴포넌트(pros.value)

  • A에 있는 버튼을 클릭해서 E컴포넌트에 있는 value를 변경하는 것
  • 상위 컴포넌트의 state 값을 props로 내려서 변경함

src/components/A.jsx

export default function A() {
  const [value, setValue] = useState('아직 안 바뀜');

  return (
    <div>
      <B value={value} />
      <button onClick={click}>E의 값을 바꾸기</button>
    </div>
  );

  function click() {
    setValue('E의 값을 변경');
  }
}

function B({value}) {
  return (
    <div>
      <p>여긴 B</p>
      <C value={value} />
    </div>
  );
}

function C({value}) {
  return (
    <div>
      <p>여긴 C</p>
      <D value={value} />
    </div>
  );
}

function D({value}) {
  return (
    <div>
      <p>여긴 E</p>
      <E value={value} />
    </div>
  );
}

function E({value}) {
  return (
    <div>
      <p>여긴 E</p>
      <h3>{value}</h3>
    </div>
  );
}

src/App.js

import './component/A';

function App() {
  return(
    <div className="App">
      <header className="App-header">
        <A />
      </header>
    </div>
  );
}

상위 컴포넌트를 변경하기

E의 Button을 클릭하여 A의 p를 변경하려면?

  1. <A />에 함수를 만들고, 그 함수 안에 state를 변경하도록 구현, 그 변경으로 인해 p 안의 내용을 변경
  2. 만들어진 함수를 props에 넣어서, <B />로 전달
  3. <B />의 props의 함수를 <C />의 props로 전달
  4. <C />의 props의 함수를 <D />의 props로 전달
  5. <D />의 props의 함수를 <E />의 props로 전달, <E />에서 클릭하면 props로 받은 함수를 실행

E컴포넌트(버튼) -> D컴포넌트 -> C컴포넌트 -> B컴포넌트 -> A컴포넌트(pros.value)

  • 상위 컴포넌트의 state 변경하는 함수 자체를 props로 전달하여
    하위 컴포넌트로 내려서 하위 컴포넌트에서 state 값을 변경함

src/components/A1.jsx

export default function A() {
  const [value, setValue] = useState('아직 안 바뀜');

  return(
    <div>
      <p>{value}</p>
      <B setValue={setValue} />
    </div>
  );
}

function B({setValue}) {
  return(
    <div>
      <p>여긴 B</p>
      <C setValue={setValue} />
    </div>
  );
}

function C({setValue}) {
  return(
    <div>
      <p>여긴 C</p>
      <D setValue={setValue} />
    </div>
  );
}

function D({setValue}) {
  return(
    <div>
      <p>여긴 D</p>
      <E setValue={setValue} />
    </div>
  );
}

function E({setValue}) {
  return(
    <div>
      <p>여긴 E</p>
      <button onClick={click}>클릭</button>
    </div>
  );

  function click() {
    setValue('A의 값을 변경');
  }
}

src/App.js

import './component/A1';

function App() {
  return(
    <div className="App">
      <header className="App-header">
        <A1 />
      </header>
    </div>
  );
}

정리

  • state를 공유하기 위해서는 상위 컴포넌트에서 state를 정의하여야 함
  • state 값을 변경하는 것을 공유하거나, state 값을 표현하기 위해 공유하는 작업에서
    상위 컴포넌트에서 정의된 state를 하위 컴포넌트가 값을 변경하는 경우 : setState 함수를 내림
    상위 컴포넌트에서 정의된 state를 하위 컴포넌트에서 값을 표현하는 경우 : state 자체를 내림
  • 컴포넌트가 복잡해질수록 이러한 state를 props 통해 내려보내는 작업에는 주의를 기울여야 함
    (실제 프로젝트에는 광범위하고 많은 컴포넌트가 있음)

6. Context API, useContext

Context API를 사용하는 이유

컴포넌트 한개정도를 거쳐서 전달하는건 사실 그렇게 큰 불편함도 없지만,
만약 3~4개 이상의 컴포넌트를 거쳐서 전달을 해야 하는 일이 발생하게 된다면 이는 매우 번거로울 것입니다.
그럴 땐, 리액트의 Context API 와 이전 섹션에서 배웠던 dispatch 를 함께 사용하면 이러한 복잡한 구조를 해결 할 수 있습니다.
리액트의 Context API 를 사용하면, 프로젝트 안에서 전역적으로 사용 할 수 있는 값을 관리 할 수 있습니다.
여기서 제가 "상태" 가 아닌 "값" 이라고 언급을 했는데요, 이 값은 꼭 상태를 가르키지 않아도 됩니다.
이 값은 함수일수도 있고, 어떤 외부 라이브러리 인스턴스일수도 있고 심지어 DOM 일 수도 있습니다. (중략)

Context API 를 사용해서 전역적으로 사용 할 수 있게 해주면 컴포넌트에게 함수를 전달해줘야 하는 상황에서
코드의 구조가 훨씬 깔끔해질 수 있습니다. 만약에 깊은 곳에 위치하는 컴포넌트에게 여러 컴포넌트를 거쳐서
함수를 전달해야 하는 일이 있다면 이렇게 Context API 를 사용하시면 됩니다.

출처 - https://react.vlpt.us/basic/22-context-dispatch.html

context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있습니다.
일반적인 React 애플리케이션에서 데이터는 위에서 아래로 (즉, 부모로부터 자식에게) props를 통해 전달되지만,
애플리케이션 안의 여러 컴포넌트들에 전해줘야 하는 props의 경우 (예를 들면 선호 로케일, UI 테마)
이 과정이 번거로울 수 있습니다. context를 이용하면, 트리 단계마다 명시적으로 props를 넘겨주지 않아도
많은 컴포넌트가 이러한 값을 공유하도록 할 수 있습니다.
출처 - https://ko.reactjs.org/docs/context.html

npx create-react-app react-context-example

하위 컴포넌트 전체에 데이터를 공유하는 방법

데이터를 Set하는 놈 (전체 공유)

  • 최상위 컴포넌트 -> Provider

데이터를 Get하는 놈 (전체 공유된 것을 사용)

  • 모든 하위 컴포넌트에서 접근 가능
  • Consumer로 하는 방법
  • class component의 this.context로 하는 방법
  • functional component의 useContext로 하는 방법

데이터를 Set 하기

  1. 일단 Context를 생성한다. (Context API)
  2. Context.Provider 컴포넌트를 사용한다.
  3. value(라는 Props)를 사용한다.

src/contexts/PersonContext.js

import React from 'react';

const PersonContext = React.createContext();

export default PersonContext;

// index.js
const persons = [
  { id: 0, name: 'Mark', age: 38 },
  { id: 1, name: 'Hanna', age: 27 },
];

ReactDOM.render(
  <PersonContext.Provider value={persons}>
    <App />
  </PersonContext.Provider>
, document.getElementById('root'));

src/index.js

import PersonContext from './contexts/PersonContext';

const persons = [
  { id: 0, name: 'Mark', age: 39},
  { id: 1, name: 'Hanna', age: 28}
];

ReactDOM.render(
  <React.StrictMode>
    <PersonContext.Provider value={persons}>
      <App />
    </PersonContext.Provider>
  </React.StrictMode>
  document.getElementById('root')
);

데이터를 Get 하기 (1) - Consumer

  1. 일단 Context를 생성한다. (Context API)
  2. Context.Consumer 컴포넌트를 사용한다.
  3. value(라는 Props)를 사용한다.

src/components/Example1.jsx

import { useContext } from 'react';
import PersonContext from '../contexts/PersonContext';

export default function Example1() {
  return(
    <PersonContext.Consumer>
      { (persons) => (
        <ul>
          {persons.map( (person) => (
            <li>{person.name}</li>
            )
          )}
        </ul>
        )}
    </PersonContext.Consumer>
  );
}

src/App.js

import Example1 from './components/Example1';

function App() {
  return(
    <div className="App">
      <Example1 />
    </div>
  );
}

export default App;

데이터를 Get 하기 (2) - class

  1. static contextType에 Context를 설정한다.
  2. this.context => value다.

여러 개 지정되지 않아 선호되지는 않음

src/components/Example2.jsx

import React from 'react';

export default class Example2 extends React.Component {
  // static contextType = PersonContext;

  render() {
    const persons = this.context;

    return(
      <ul>
        {persons.map( (person) => (
          <li>{person.name}</li>
          )
        )}
      </ul>
    );
  }
}

Example2.contextType = PersonContext; // static 대신

src/App.js

import Example2 from './components/Example2';

function App() {
  return(
    <div className="App">
      <Example2 />
    </div>
  );
}

export default App;

데이터를 Get 하기 (3) - functional

★ 가장 많이 쓰이는 방식
★ 요즘은 대부분 hook으로 개발하기 때문에 꼭 알아둬야 하는 중요한 개념

  1. useContext로 Context를 Props로 호출한다.
  2. useContext의 return이 value다.

src/components/Example3.jsx

import { useContext } from 'react';
import PersonContext from '../contexts/PersonContext';

export default function Example3() {
  const persons = useContext(PersonContext);

  return(
    <ul>
      {persons.map( (person) => (
        <li>{person.name}</li>
        )
      )}
    </ul>
  );
}

src/App.js

import Example3 from './components/Example3';

function App() {
  return(
    <div className="App">
      <Example3 />
    </div>
  );
}

export default App;

참고자료

https://dev-yakuza.posstree.com/ko/react/context-api/
https://hyeok999.github.io/2020/02/14/ReactStudy-05/#a8
https://goforit.tistory.com/132?category=953050

profile
필요한 내용을 공부하고 저장합니다.

0개의 댓글