08.hooks

Updated:


1.React Hooks 이론

  • ReactHooks 는 class 없이 state 를 사용할 수 있는 새로운 기능입니다.

Hooks 가 필요한 이유

항상 기술은 그 전에 것의 불편함이나 문제점을 해결하기 위해서 더욱 발전합나디ㅏ.

그와 같이 React Hooks 도 주로 Class Component 로 사용되어온 React 에서 느껴왔던 불편함이나 문제점들을 해결하기 위해서 개발 되었습니다.

원래는 React는 주로 Class Component 를 사용하고 React Hooks는 Functional component 를 사용합니다.

  • React lifecycle

image

Class Component 에서는 생명주기를 이용할 때 componentDidMount, ComponentDidUpdate, componentWillUnmount 이렇게 다르게 처리를 해주지만 react hooks 를 사용할 때는 useEffect 안에서 다 처리를 해줄수 있기 때문입니다.

2.useState

  • state를 대체 해서 function component

  • useState 는 두번째 있는 setState 는 값이 변경 될때 마다 re-render 가 된다.

const [item, setItem] = useState(1);

위에 코드에서 useState 는 number 1 을 초기 값으로 하고 return 으로 arr 를 2가지 value 를 return 합니다.

첫번째는 item은 초기값인 1을 저장하는 값이고 setItem 은 변경을 위한 value 값이라고 보면 됩니다.

import React, { useState } from "react";
import "./App.css";

const App = () => {
  const [item, setItem] = useState(1);
  const incrementItem = () => setItem(item + 1);
  const decrementItem = () => setItem(item - 1);

  return (
    <div className="App">
      <h1>Hello {item}</h1>
      <h2>Start editing to see some magic happen!</h2>

      <button onClick={incrementItem}>Increment</button>
      <button onClick={decrementItem}>Decrement</button>
    </div>
  );
};

export default App;

3.useEffect

  • 라이프 사이클 훅을 대체할 수 있다 (componentDidMount, componentDidUpdate, componentWillUmount)

  • 2개의 parameter 를 받는데, 첫번째는 function 으로써의 effect, 두번째는 dependency 임

예시

// sayHello 라는 함수가 변할때 (render) 될때 마다 실행되는대 dependency 가 number 인 것만 의존적으로 변경된다는 의미임
// componentDidMount 와 componentWillUpdate 인데 number 일때만 업데이트를 함
useEffect(sayHello, [number]);

// 두번째 인자로 빈 arr 을 사용하게 되면 초기 실행 할때 1번만 실행하게 됨
useEffect(sayHello, []);
  • useEffect 는 componentDidMount, componentWillUnMount, componentDidUpdate 을 functional component 에서 사용할 수 있게 해줌

4.custom Hooks

  • 사용자의 편의에 의해서 hook 을 재작해서 사용하는 것

HOC 대체

  • HOC component 를 Custom Hooks 로 대체해서 많은 Wrapper component 를 줄일 수 있게 됨

HOC (Higher Order Component)란?

화면에서 재사용 가능한 로직만을 분리해서 component로 만들고, 재사용 불가능한 UI 와 같은 다른 부분들은 parameter 로 받아서 처리하는 방법입니다.

  • 예를 들어 유저 리스트를 가져오는 공통적인 부분을 HOC 로 만들어서 넣어주고, 그 HOC 를 각각 컴포넌트로 감싸주면 모든 컴포넌트가 따로 인증을 위한 부분은 넣지 않아서 중복된 코드들을 반복적으로 처리 할 수 있습니다. Hooks 가 나오기 전에 class component 에서는 HOC 를 주로 사용했습니다. 그러나, 문제는 코드가 복잡하고 길어지게 되면 많은 Wrapper component 가 생길 수 있다는 것입니다. Wrapper 가 너무 많아 지게 되면 데이터 흐름을 파악하기가 힘들어 집니다.
// Wrapper 예시

<LanguageHOC>
  <ThemeHOC>
    <AuthHOC>
      <APage />
    </AuthHOC>
  </ThemeHOC>
</LanguageHOC>

이러한 문제들은 Hooks 의 Custom Hooks 를 사용해서 해결 할 수 있습니다.

useWindowWidth

// window 창이 resize 될때마다 width 값을 return하는 custom hook

import { useEffect, useState } from "react";

function useWindowWidth() {
  // 현재 브라우저의 width 값의 초기화 값임
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    // resize 함수 선언
    const resize = () => {
      setWidth(window.innerWidth);
    };
    // window 에 resize 가 일어나면 resize 함수가 실행 되게 이벤트 설정
    window.addEventListener("resize", resize);
    // cleanUP
    return () => {
      window.removeEventListener("resize", resize);
    };
    // dependency 설정
  }, []);

  return width;
}

export default useWindowWidth;

5.useReducer

  • useState 의 확장판임

  • 다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우

  • 다음 state 가 이전 state 에 의존적인 경우

  • Redux 를 안다면 쉽게 사용 가능

// useReducer sample

import { useReducer } from "react";

// reducer - state 를 변경하는 로직이 담겨 있는 함수 이전 state 에 의존적일때 사용하기 좋음
const reducer = (state, action) => {
  if (action.type === "PLUS") {
    return {
      count: state.count + 1,
    };
  }
  return state;
};

// dispatch - action 객체를 넣어서 실행

// action 은 객체이고 필수 프로퍼티로서 type 을 가진다

const Example6 = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>You clicked {state.count} times</p>
      <button onClick={click}>Click me</button>
    </div>
  );

  function click() {
    dispatch({ type: "PLUS" });
  }
};

export default Example6;

6.useMemo, useCallback, useRef

  • render 사이에서 어떠한 상태를 유지하기 위한 기능임

  • class component 를 사용할때는 계속 render method 만 돌기 때문에 class 안에 있는 것들이 유지가 되었는데, function 은 생성이 되면서 그안에 있는 것들은 새로 생성되는 경향이 있기때문에 render 사이에 어떠한 값을 저장하고 보관 하는 용도로 사용되는 hooks (useMemo, useCallback, useRef)라고 보면 됨

useMemo , useCallback

// useMemo, useCallback, useRef 사용하기

import { useCallback, useMemo, useState } from "react";

const sum = (persons) => {
  console.log("sum...");
  return persons.map((person) => person.age).reduce((a, b) => a + b, 0);
};

const Example7 = () => {
  const [value, setValue] = useState();
  const [persons] = useState([
    { name: "Jacob", age: 30 },
    { name: "John", age: 20 },
  ]);

  // 반복된 사용을 막기 위해 useMemo 사용
  // Persons 에 의존적인 count 는 다시 계산이 안되게 하는것 => react를 최적화 하는데 도움이 많이 됨
  const count = useMemo(() => {
    return sum(persons);
  }, [persons]);

  // useCallback 은 안에 들어있는 함수를 언제 새로 세팅해 줄건지를 dependency 리스트에 의존적으로 결정해서 클릭안으로 넣어 줄건지를 정해주는것
  const click = useCallback(() => {
    console.log(value);
  }, []);

  const change = (e) => {
    setValue(e.target.value);
  };

  return (
    <div>
      <input value={value} onChange={change} />
      <p>{count}</p>
      <button onClick={click}>Click</button>
    </div>
  );
};

export default Example7;

useRef

//  useRef 사용하기

import { createRef, useRef, useState } from "react";

const Example8 = () => {
  const [value, setValue] = useState();

  // createRef 로 만든 input1Ref 는 항상 유지하는것이 아니라 render 될때 마다 새로 reference 를 만들어서 render 가 될때 마다 input에 넣어주는것
  // 즉, render 될때만 넣어주는것
  const input1Ref = createRef();

  // useRef 로 만든 input2Ref 는 render 를 돌아도 계속 유지를 하는것 대신에 첫번째는 진행된것이 없으니까 undefined 가 나온거고 render 가 진행되면 생성된 reference 값을 나타내게 됨
  // render 사이에도 그것을 유지해주는 것
  const input2Ref = useRef();

  console.log(input1Ref.current, input2Ref.current);

  const change = (e) => {
    setValue(e.target.value);
  };

  return (
    <div>
      <input value={value} onChange={change} />
      <input ref={input1Ref} />
      <input ref={input2Ref} />
    </div>
  );
};

export default Example8;

7.React router hooks

React router hooks 자세히보기

useHistory

  • useHistory는 일반적인 history 객체와 똑같은 객체를 가지므로 사용법이 동일하며, 사용하기 위한 준비단계는 거의 없습니다.
import React from "react";
import { useHistory } from "react-router-dom";

const Home = () => {
  const history = useHistory();
  // history를 props에서 얻어왔을 때 처럼 동일하게 사용 가능하다.

  return (
    <div onClick={() => history.push("/auth")}>
      <div>Hello!</div>
    </div>
  );
};

export default Home;

useLocation

  • useLocation hooks는 사용자가 현재 머물러있는 페이지에 대한 정보를 알려주는 hooks 입니다. 이 hooks는 defaultProps 하나인 location 객체를 대체 하는 react-router-dom hooks 입니다.
import React from "react";
import { useLocation } from "react-router-dom";

const Home = () => {
  const location = useLocation();
  console.log(location); // pathname과 search라는 객체가 출력됩니다.

  return <></>;
};

export default Home;
  • search는 쿼리스트링을 분석해야하는 상황에서 유용합니다. 앞에서 pathname이 출력했던 부분을 제외한 쿼리스트링이 출력되며, 결과는 ?keyword=리액트 라는 출력결과가 나옵니다. 출력된 값은 query-string이라는 쿼리스트링 파싱 라이브러리를 이용하여 제대로 된 값을 얻어낼 수 있습니다.
import React from "react";
import { useLocation } from "react-router-dom";
import queryString from "query-string";

const Home = () => {
  const { search } = useLocation();
  // search: ?keyword=리액트

  const { keyword } = queryString.parse(search);
  // keyword 출력결과: "리액트"

  return <></>;
};

export default Home;

useParams

  • useLocation 과 비슷한 hooks 인데 왜냐하면 useLocation은 쿼리스트링 정보를 얻는데 유용하다고 하면, useParams는 path parameter의 정보를 얻을 수 있는 hooks이기 때문입니다.

  • 먼저, useParams를 사용하기 위해서 동적 라우팅 설정을 해주어야 합니다. 설정법은 App.js에서 다음과 같이 내용을 추가해주시면 됩니다.

// App.js

import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Home from "components/Home";

const App = (): JSX.Element => {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/home/:id" component={Home} />
        {/* id라는 동적 라우팅값을 걸어주었다. */}
      </Switch>
    </BrowserRouter>
  );
};

export default App;
  • : (콜론)을 이용하여 다음과 같이 설계해놓으면, /home/ 뒤에 1이 오든, 2가 오든, 혹은 문자열이 오든 상관없이 라우팅이 정상 작동됩니다.

  • 이제 Home 컴포넌트에서 path parameter ( 동적 라우팅 값 )을 읽어오도록 하겠습니다. 기존에는 match props를 이용하여 match.params.id의 형식으로 접근을 해야 했지만, useParams를 이용하면 좀 더 간단하게 접근 가능합니다.

// in home.js

import React from "react";
import { useParams } from "react-router-dom";

const Home = () => {
  const { id } = useParams();
  // 동적 라우팅 값으로 걸어둔 이름으로 객체를 가져올 수 있다.

  // 현재 주소의 값이 http://localhost:3000/home/3 일때
  console.log(id); // "3"이 출력된다.

  return <></>;
};

export default Home;

🔶 🔷 📌 🔑

Reference

Categories:

Updated:

Leave a comment