React | Hooks 02 ( useReducer / useMemo / useCallback / useRef / 커스텀 hooks)

Kate Jung·2021년 10월 30일
0

React

목록 보기
3/28
post-thumbnail

🚩 useReducer

useReducer 란?

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

리듀서 란?

현재 상태, 액션 값(업데이트를 위해 필요한 정보를 담은) 전달 받아 새로운 상태 반환하는 함수

  • 새 상태 만들 때 → 불변성 지켜야 함.

useReducer 의 액션 객체

→ 어떤 값도 가능 (type 없거나, 문자열/숫자여도 ok.)

예시 코드

1. 기본

        import React, { useReducer } from "react";
        
        function reducer(state, action) {
          // action.type에 따라 다른 작업 수행
          switch (action.type) {
            case "INCREMENT":
              return { value: state.value + 1 };
            case "DECREMENT":
              return { value: state.value - 1 };
            default:
              // 아무것도 해당되지 않을 때, 기존 상태 반환
              return state;
          }
        }
        
        const Counter = () => {
        const [state, dispatch] = useReducer(reducer, { value: 0 });
        
          return (
            <div>
              <p>
                현재 카운터 값은 <b>{state.value}</b> 입니다.
              </p>
              <button onClick={() => dispatch({ type: "INCREMENT" })}>+1</button>
              <button onClick={() => dispatch({ type: "DECREMENT" })}>-1</button>
            </div>
          );
        };
        
        export default Counter;

2. 인풋의 경우

        import React, { useReducer } from "react";
        
        function reducer(state, action) {
          return {
            ...state,
            [action.name]: action.value,
          };
        }
        
        const Info = () => {
          const [state, dispatch] = useReducer(reducer, {
            name: "",
            nickname: "",
          });
          const { name, nickname } = state;
          const onChange = (e) => {
            dispatch(e.target);
          };
        
          return (
            <div>
              <div>
                <input name="name" value={name} onChange={onChange} />
                <input name="nickname" value={nickname} onChange={onChange} />
              </div>
              <div>
                <div>
                  <b>이름:</b> {name}
                </div>
                <div>
                  <b>닉네임: </b> {nickname}
                </div>
              </div>
            </div>
          );
        };
        
        export default Info;

파라미터

  • 1번째: 리듀서 함수
  • 2번째: 해당 리듀서의 기본 값

장점

컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다.

🚩 useMemo

연산 최적화 (함수형 컴포넌트 내부)

→ 렌더링 하는 과정에서 특정 값이 바뀌었을 때만 연산 실행

  • 예시 코드
    import React, { useState, useMemo } 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("");
    
      const onChange = (e) => {
        setNumber(e.target.value);
      };
      const onInsert = () => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber("");
      };
    
    	// list 배열의 내용이 바뀔 때만 getAverage 호출
      const avg = useMemo(() => getAverage(list), [list]);
    
      return (
        <div>
          <input value={number} onChange={onChange} />
          <button onClick={onInsert}>등록</button>
          <ul>
            {list.map((value, index) => (
              <li key={index}>{value}</li>
            ))}
          </ul>
          <div>
            <b>평균값:</b> {avg}
          </div>
        </div>
      );
    };
    
    export default Average;

🚩 useCallback

만들어둔 함수 재사용 가능 (렌더링 성능 최적화)

  • 파라미터

    • 첫 번째

      생성하고 싶은 함수

    • 두 번째

      배열 (어떤 값이 바뀌었을 때 함수를 새로 생성해야 하는지 명시)

      • 빈 배열

        컴포넌트가 렌더링될 때 만들었던 함수 → 재사용

      • 배열 값이 있어야 할 경우

        함수 내부에서 상태 값에 의존해야 할 때

        → 그 값을 반드시 두 번재 파라미터 안에 포함 시켜야 함.

  • 예시 코드

    • 기존 코드
      • 컴포넌트가 리렌더링될 때마다 새 함수 사용
        → 컴포넌트 렌더링 자주 발생 or 렌더링할 컴포넌트 수 多 경우: 최적화 추천
    import React, { useState, useMemo, useCallback } 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("");
    
      const onChange = useCallback((e) => {
        setNumber(e.target.value);
      }, []); // 컴포넌트가 처음 렌더링될 때만 함수 생성
      const onInsert = useCallback(() => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber("");
      }, [list, number]); // list 혹은 number가 바뀌었을 때만 함수 생성
    
      const avg = useMemo(() => getAverage(list), [list]);
    
      return (
        <div>
          <input value={number} onChange={onChange} />
          <button onClick={onInsert}>등록</button>
          <ul>
            {list.map((value, index) => (
              <li key={index}>{value}</li>
            ))}
          </ul>
          <div>
            <b>평균값:</b> {avg}
          </div>
        </div>
      );
    };
    
    export default Average;

🚩 useRef

  • useRef를 사용하여 ref 설정 시
    → useRef를 통해 만든 객체 안의 current 값이 실제 엘리먼트를 가리킴

  • 예시 코드

    import React, { useRef } from "react";
    
    (생략)
    
    const Average = () => {
      const inputEl = useRef(null);
    	(생략)
    
      const onInsert = useCallback(() => {
    		(생략)
        inputEl.current.focus();
      }, [list, number]); // list 혹은 number가 바뀌었을 때만 함수 생성
    
      return (
        <div>
          <input value={number} onChange={onChange} ref={inputEl} />
          <button onClick={onInsert}>등록</button>
    		</div>

로컬 변수 사용

  • 로컬 변수란?

    렌더링과 상관없이 바뀔 수 있는 값

  • 컴포넌트 로컬 변수 사용 시에도 useRef 활용 가능

  • 주의

    • ref안의 값이 바뀌어도 컴포넌트가 렌더링 되지 x → 렌더링 관련x 값 관리할 때만 사용할 것
  • 코드 예제 (함수형)

    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;

🚩 커스텀 hooks

  • 여러 컴포넌트에서 비슷한 기능 공유 시
    → Hooks 제작 → 로직 재사용 가능

  • 예시 코드

    useInput.js

    • 기존 Info 컴포넌트에서 여러 개의 인풋을 관리하기 위해 useReducer로 작성했던 로직
      useInput 라는 Hook으로 분리
        import { useReducer } from "react";
        
        function reducer(state, action) {
          return {
            ...state,
            [action.name]: action.value,
          };
        }
        
        export default function useInput(initialForm) {
          const [state, dispatch] = useReducer(reducer, initialForm);
          const onChange = () => {
            dispatch(e.target);
          };
          return [state, onChange];
        }

Info.js

        import React from "react";
        import useInputs from "./useInputs";
        
        const Info = () => {
        const [state, onChange] = useInputs({
            name: "",
            nickname: "",
          });
          const { name, nickname } = state;
        
          return (
            <div>
              <div>
                <input name="name" value={name} onChange={onChange} />
                <input name="nickname" value={nickname} onChange={onChange} />
              </div>
              <div>
                <div>
                  <b>이름:</b> {name}
                </div>
                <div>
                  <b>닉네임: </b> {nickname}
                </div>
              </div>
            </div>
          );
        };
        
        export default Info;

  • 참고
    • 리액트를 다루는 기술_김민준
profile
복습 목적 블로그 입니다.

0개의 댓글