본문 바로가기
React

2021.07.23 React Hooks

by 해맑은 코린이 2021. 7. 23.

2021.07.23 React Hooks_정리노트 

 

오느른~~~~~ 리액트를 다루는 기술 8장이다 8장 !! 나름 꾸준히 보는즁 ㅎㅅㅎ 

드디어 나타난 함수형 컴포넌트에서 생긴 Hooks 들을 정리할 예정 제일 궁금했던 곳인만큼 걱정 반 + 기대반으로 정리 시작!

 

 

Hooks

리액트 16.8v 부터 새로 도입된 기능. 함수형 컴포넌트에서도 상태 관리를 할 수 있으며, 렌더링 직후 작업을 설정하는 등, 기존의 클래스형 컴포넌트에서만 가능했던 기능들을 이제는 함수형에서도 할 수 있게 되었음!

 

책에서도 새로운 리액트 프로젝트를 팠으니 나도 똑같이 새로 생성하고 하기

$ yarn create-react-app hooks-tutorial

 

6개의 Hook 과 함께 커스텀 Hook 사용하는 것까지 ㄱ ㄱ

 

 


useState

가장 기본적인 Hook, 함수형 컴포넌트에서도 가변적인 상태를 지닐 수 있게 해줌. props, state 편에서 다뤘지 이미 음음

복습 겸 쓰는 방법부터, 여러 개까지 한번에 써봅시단!

 

 

src/Info.js ( 새로 생성 )

// useState import 
import React, { useState } from "react";

const Info = () => {
// [ 현재 상태, 상태를 바꾸는 세터 함수 ] 배열 비구조화 할당 
// 하나의 state 당 하나의 상태를 관리. 여러개의 상태를 관리하고 싶다면, 여러개의 useState 사용 가능.
  const [name, setName] = useState("");
  const [nickname, setNickname] = useState("");

//   이벤트시 호출될 함수 작성 
  const onChangeName = (e) => {
    setName(e.target.value);
  };

  const onChangeNickname = (e) => {
    setNickname(e.target.value);
  };


 
  return (

    <div>
        <div>
            {/* value 값과 event 호출 함수 적어주기  */}
            <input value={name} onChange={onChangeName} />
            <input value={nickname} onChange={onChangeNickname} />
        </div>
        <div>
            <b>이름 - </b> {name}
            <b>닉네임 - </b> {nickname}
        </div>
  </div>
  )
  ;
};

export default Info;

 

이 내용이 좀 더 자세하게 궁금하면, 

 

https://korinkorin.tistory.com/60

 

2021.07.11 React props,state

2021.07.11 React props,state_정리노트 홀홀 분명 예ㅔㅔㅔ전에 똑같은글을 봤다는 기억이 난다면 모두 지우시게 예전에 제일 열심히 정리하고 제일 기본적이면서 필수적인 컴포넌트 기능이라고 생각

korinkorin.tistory.com

 

이쪽으로 ! ㅎㅅㅎ

 

여기서 자세히 정리해놨음 ! 

 

이제 실행해보면!

 

 

이렇게 input 의 value 값이 바뀔 때마다 값이 잘 뜬다!! 

 

 


 

useEffect 

리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정하는 Hook

클래스형 컴포넌트의 componentDidMount , componentDidUpdate 를 합친 형태로 봐도 무방!

기본적으로는 렌더링 되고 난 직후마다 실행되며, 두 번째 파라미터 배열에 무엇을 넣는지에 따라 실행되는 조건이 달라진다.

 

 

 

src/Info

이렇게 적어주고, 

 

 

인풋의 내용을 변경하면 이렇게 잘 적용이 된 모습을 볼 수 있다.

 

 

또 맨 처음 렌더링 될 때만 수행하고 싶다면 ? ( 마운트 될 때 )

 

 

 

이렇게 useEffect 두번째 인자로 빈 배열을 넣어주면 된다!

그러면 맨 처음 렌더링 될 때만 실행하고, 인풋의 내용이 바뀌어도 해당 내용은 출력되지 않는다.

 

 

또 만약에, 특정 값이 변경될 때만 호출하고 싶을 경우에는 ? ( input value 가 변경될 때만 실행하고 싶거나 등등... )

 

 

 

이렇게 배열 안에 검사하고 싶은 값을 넣어주면 된다. 이 배열 안에는 useState 를 통해 관리하고 있는 상태를 넣어주어도 되고, props 로 전달받은 값을 넣어주어도 된다. 

 

클래스형 컴포넌트에서는 이렇게 적어주겠지!! 똑같음 똑같음

 

 

 

이렇게 name 의 값이 업데이트 될 때마다 콘솔에 찍힌다!

 

 

컴포넌트가 언마운트 되기 전이나, 업데이트 되기 직전에 어떠한 작업을 수행하고 싶다면...!!!! 

 

뒷정리 함수를 반환해 주어야 한다. 

말그대로 뒷정리를 해준다고 이해하면 되고, useEffect 에서 함수를 반환해줄 것이다!

 

 

src/App

 

App 에다가 Info 컴포넌트를 보여주고 숨기는 버튼을 만들어주고 ,

 

 

 

숨기기 버튼을 누르면 clean-up 이 나타난다! 또 , name 의 값이 바뀌면 

 

 

 

이렇게 렌더링 될 때 마다 뒷정리 함수가 나타난다. 그리고 뒷정리 함수가 호출될 때는 업데이트 되기 직전의 값을 출력해서 보여준다! 

 

마운트와 마찬가지로 언마운트될 때만 뒷정리 함수를 호출하고 싶다면, useEffect 함수의 두 번째 파라미터에 비어 있는 배열을 넣으면 된다.

 

 

이렇게 실행하면, 

 

버튼을 숨길 때만 뒷정리 함수인 clean-up 이 호출된다. 

 

 


 

useReducer

useState 보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 해주고 싶을 때 사용하는 Hook.

리듀서라는 개념은 나ㅏㅏ중에 리덕스를 배울 때 자세히 정리해보겠움...!! 오늘은 간단하게 이런거다~ 하고 알아가기

리듀서는 현재 상태, 업데이트를 위해 필요한 정보를 담은 action 값을 전달받아 새로운 상태를 반환하는 함수이다.

리듀서 함수에서 새로운 상태를 만들 때는 반드시 불변성을 지켜주어야함!

 

 

useReducer 의 가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 것!!

 

 

 

src/Counter.js ( 새로 생성 )

 

기존에 만들었던 Counter 컴포넌트를 재사용해도 되고, 나는 새로 프로젝트를 파서 새로 다시 만들어주었음.

 

// useReducer import
import React, { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { value: state.value + 1 };
    case "DECREMENT":
      return { value: state.value - 1 };
    default:
      return state;
  }
}

const Counter = () => {
  // useReducer(리듀서 함수, 해당 리듀서의 기본값)
  // Hook 을 사용하면 state(현재 가리키고 있는 상태) 와 dispatch(액션을 발생시키는 함수) 함수를 받아온다. 
  //  dispath(action) 과 같은 형태. 함수 안에 파라미터로 액션 값을 넣어주면 리듀서 함수가 호출되는 구조
  const [state, dispath] = useReducer(reducer, { value: 0 });
  return (
    <div>
      <p>
        현재 카운터 값은 <b>{state.value}</b>
      </p>
      <button onClick={()=>dispath({type: "INCREMENT"})}>+1</button>
      <button onClick={()=>dispath({type: "DECREMENT"})}>-1</button>
    </div>
  );
};

export default Counter;

 

App 에서 

 

컴포넌트를 불러와주고, 

 

 

실행해주면, 전과 같이 잘 작동한당

 

 

 

이제 우리가 여러개의 상태를 관리해줄 때, useState 여러개 썼던 것 처럼 리듀서로 인풋의 상태를 관리해줘보자.

 

useReducer 를 사용하면, 기존 클래스형 컴포넌트에서 setState 를 하는 것과 유사한 형태로 작업을 처리할 수 있다.

 

 

 

src/Info

// useReducer import
import React, { useState, useEffect, useReducer } from "react";

function reducer(state, action) {
  return {
    ...state,
    [action.name]: action.value,
  };
}

const Info = () => {
  const [state, dispath] = useReducer(reducer, {
    name: "",
    nickname: "",
  });

  const { name, nickname } = state;
  // const [name, setName] = useState("");
  // const [nickname, setNickname] = useState("");
  // componentDidUpdate(prevProps,prevState){
  //   if(prevProps.value !== this.props.value){
  //     doSomething()
  //   }
  // }
  // useEffect(() => {
  //   console.log("name state 값이 업데이트 될때");
  // }, [name]);

  useEffect(() => {
    console.log("effect");

    return () => {
      console.log("clean-up");
    };
  }, []);
  // const onChangeName = (e) => {
  //   setName(e.target.value);
  // };

  // const onChangeNickname = (e) => {
  //   setNickname(e.target.value);
  // };

  const onChange = (e) => {
    dispath(e.target);
  };
  return (
    <div>
      <div>
     
        {/* <input value={name} onChange={onChangeName} />
        <input value={nickname} onChange={onChangeNickname} /> */}
        <input name="name" value={name} onChange={onChange} />
        <input name="nickname" value={nickname} onChange={onChange} />
      </div>
      <div>
        <b>이름 - </b> {name}
        <b>닉네임 - </b> {nickname}
      </div>
    </div>
  );
};

export default Info;

 

이렇게 비교하려고 useState 를 쓴 부분은 주석 처리했다.

 

이렇게 기존 클래스형처럼 input 태그에 name 값을 할당해서, e.target.name 을 참조하여 state 값을 업데이트 해 준 것과 굉장히 유사함!! 

 

액션 값은 그 어떤 값도 사용 가능해서, 이벤트 객체가 지니고 있는 e.target 값 자체를 액션 값으로 사용했다.

 

오오 이거는 인풋이 여러개면 여러개일수록 코드를 깔끔히 작성할 수 있겠다!!!!

 

 

 

작동도 잘 됨 !

 


useMemo

함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있는 Hook.

 

평균을 구하는 컴포넌트를 만들어서 활용해봅씨단.

 

src/Average.js ( 새로 생성 )

import React, { useState } from "react";

// 평균값 구하는 함수
const getAverage = (numbers) => {
  console.log("평균값 게산 중..");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");
// onChange 이벤트로 input  value 를 가져옴.
  const onChange = (e) => {
    setNumber(e.target.value);
 
  };
// input value 는 문자열이기 때문에, 숫자로 변형해서 list 의 값을 업데이트 해줌. list가 업데이트 되면, 인풋에 적었던 내용은 빈 값으로 돌려놓음.
  const onInsert = (e) => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber("");
  };
  return (
    <div>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}> 등록 </button>
      {/* index 번호로 key 값 설정. input value 을 내용으로 출력*/}
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
          {/* 업데이트 된 숫자형 배열 list의 평균을 구하는 함수 호출 */}
        <b>평균값-</b>
        {getAverage(list)}
      </div>
    </div>
  );
};

export default Average;

 

음..^^ 거참 돌아가는거 한번 복잡하네 허허 일단 주석으로 흐름 대충 정리했으니까 간단히 다시 정리하면, input value 값을 가져와서 숫자형 리스트로 만든 다음, 평균값을 구하는 로직의 함수를 list 값을 담아서 호출해준다. 

 

src/App

 

 

불러와주고,

 

 

 

 

실행하면, 평균이 잘 계산되는 모습을 볼 수 있는데, 숫자를 등록할 때 뿐만 아니라 인풋의 내용이 수정될 때도, 지금 getAverage 함수가 호출되고 있다. 내용이 바뀔 때는 평균값을 계산하는 게 아닌데, 렌더링이 계속 된다면 이것은 낭비다!! 

 

이제 useMemo Hook 을 사용해서 최적화해봅씨단.

 

 

 

렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 원하는 값이 바뀌지 않았다면, 이전에 연산했던 결과를 다시 사용하는 방식으로, list 배열이 바뀔 때만 해당 함수를 실행해라~ 라고 쓸 수 있다. 

 

이제 인풋의 내용이 바뀌더라도 해당 함수는 실행되지 않는다!

 

 


 

useCallback

useMemo 을 기반을로 만들어진 Hook. 주로 렌더링 성능을 최적화해야 하는 상황에서 사용.

useCallback(fn, deps) useMemo(() => fn, deps)와 같은데, 함수를 이용하기 편리하기 위해서 나타났으며, 해당 Hook 은 만들어놨던 함수를 재사용할 수 있음.

 

src/Average

 

앞서 우리가 만들었던 onChange, onInsert 함수는 컴포넌트가 리렌더링 될 때마다 새로 만들어진 함수를 사용하게 되는데, 이렇게 useCallback 을 사용하게 되면, 이를 최적화할 수 있다. 

useCallback 의 첫번째 파라미터에는 생성하고 싶은 함수를 넣고, 두 번째 파라미터에는 배열을 넣으면 된다. 

이 배열에는 첫 렌더링에만 실행할 때는 빈 배열을, 어떤 값이 바뀔 때 함수를 새로 생성해야할 때는 그 값을 배열에 명시해 주면 된다 .

함수 내부에서 상태 값에 의존해야할 때는 (state 값을 사용하게 되면 ) 그 값을 반드시 두 번째 파라미터 안에 포함시켜 주어야 한다. 

 

여기서 보면, onChange 에는 기존의 값을 조회하지 않았기 때문에, 빈 배열을 넣어주면 되지만

onInsert 에서는 list 와 number 의 값을 조회하고  있으므로, 배열안에다가 이 두 값을 꼭 넣어주어야 한다.

 

 

 


 

 

useRef

으으.. 5장에서 어려웠던 ref 값 조회가 나왔다. 그 때 잠시 언급했던 것처럼 createRef 와 형태는 유사하다.

이 Hook 은 함수형 컴포넌트에서 ref 를 쉽게 사용할 수 있도록 해준다.

 

 

src/Average

 

 

 

 

 

 

이렇게 하면, 등록 버튼을 눌렀을 때, 해당 input 을 가리키기 때문에 해당 인풋에 포커스가 된다.

 

 

 

 

이렇게..!!!

 

 

 

로컬변수( 렌더링과 상관 없는 값 )를 사용해야할 때도, useRef 를 활용할 수 있다! 

import React,{ useRef } from 'react';

const RefSample = () =>{
	const id = useRef(1);
    const setId = (n) =>{
    id.current = n;
    }
    
    const printId = () =>{
    console.log(id.current);
    }
    
    return (
    <div>
    	refsample
    </div>
    );
    
   };
   
   
   export default RefSample;

 

이렇게 useRef 를 활용해서 적을 수 있다.

다만 ref 안의 값이 바뀌어도 컴포넌트가 렌더링되지 않는다. 렌더링과 관련되지 않은 값을 관리할때만 이러한 방식을 쓰자!!!

 

 

 


custom Hook

후.. 길고 길었던 Hook 에서 마지막.. ㅎ ㅏ... 

여러 컴포넌트에서 비슷한 기능을 공유할 경우 Hook 을 내가 직접 커스텀할 수 있다. 

 

 

 

src/useInput.js ( 새로 생성 )

 

 

 

아까 위에서 Info 컴포넌트에서 여러 개의 인풋을 관리하기 위해 사용했던 useReducer 로 작성했던 로직을 따로 custom Hook 으로 분리할 것임.

 

 

src/Info

useInput 을 불러와주고, reducer 함수 지움. 그리고 state 와 onChange 를 가져와서 useInput Hook사용.

 

 

끝임 끝. wow  겁나 깔끔해져따

 

 

역시 물론 멋진 개발자분들께서  custom Hook을 이미 만들어놓은 것을 우리는 라이브러리로 설치해서 사용할 수 있다. 

 

https://nikgraf.github.io/react-hooks/

 

Collection of React Hooks

 

nikgraf.github.io

https://github.com/rehooks/awesome-react-hooks

 

GitHub - rehooks/awesome-react-hooks: Awesome React Hooks

Awesome React Hooks. Contribute to rehooks/awesome-react-hooks development by creating an account on GitHub.

github.com

 

멋져..^^

 


 

와 진짜 개길어...ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 오늘은 이런것이 있다~ 하면서 책 내용 따라치면서 흐름을 혼자서 둗두다다다다다 쳤는데, 정말 함수형 컴포넌트에서 이제 거의 다 할 수 있도록 만든 것 같음 .

리액트에서 클래스형 컴포넌트로 이미 만들어 놓은 프로젝트의 경우 계속 클래스형을 지원할거기 때문에 굳이 바꿀 필요는 없지만,

새로 시작하는 프로젝트에서는 간단한 함수형 컴포넌트를 권장한다네. 나는 이제 함수형으로 열심히 같이 토닥토닥 해야지 ㅎㅅㅎ

 

 

오늘도.. 너무 길었기 떄문에 끝끄트트트틍...급하게 마무리!

'React' 카테고리의 다른 글

2021.07.28 React To-do List  (0) 2021.07.29
2021.07.24 React styling  (2) 2021.07.24
2021.07.19 React Life Cycle  (2) 2021.07.21
2021.07.19 React Component 반복  (1) 2021.07.19
2021.07.16 React ref  (0) 2021.07.16

댓글