Hooks

ho_vi·2023년 9월 19일

React

목록 보기
8/19
post-thumbnail

리액트 v16.8에 새로 도입된 기능으로서, 함수형 컴포넌트에서도 상태 관리를 할수 있는 useState, 그리고 렌더링 직 후 설정하는 useEffet등의 기능을 제공하여 기존의 함수형 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있도록 함.

Hook은 함수 컴포넌트에서 React state와 생명주기 기능을 연동 할 수 있게 해주는 함수 입니다. Hook은 class 안에서는 동작하지 않습니다.

특징

  • 선택적 사용 기존의 코드를 다시 작성할 필요 없이 일부의 컴포넌트들 안에서 Hook을 사용할 수 있습니다.
  • 100% 이전 버전과의 호환성 Hook은 호환성을 깨뜨리는 변화가 없습니다.
  • 현재 사용 가능 Hook은 배포 v16.8.0에서 사용할 수 있습니다.

useState

useState()는 리액트에서 상태(state)를 관리하기 위해 제공되는 훅(hook) 중 하나입니다.

useState()를 사용하면 함수형 컴포넌트에서도 상태를 관리할 수 있으며, 상태가 변경될 때마다 컴포넌트가 자동으로 다시 렌더링됩니다.

useState()는 배열 형태로 반환되며, 첫 번째 요소는 현재 상태이고, 두 번째 요소는 상태를 변경하는 함수입니다. 상태를 변경하는 함수는 setState()와 비슷한 역할을 하며, 새로운 상태를 인자로 받아 컴포넌트를 다시 렌더링합니다.

useState 기본

useState는 가장 기본적인 Hook이며, 함수 컴포넌트에서도 가변적인 상태를 지닐 수 있게 해 줍니다. (함수형 컴포넌트에서 상태를 관리해야 하는 경우 사용)

  • useState 함수의 초기값을 0으로 설정
  • UseState 함수가 호출되면 배열을 반환하는데 첫 번째 원소는 상태값, 두 번째 원소는 상태를 설정하는 함수
import React, { useState } from 'react';

const App = () => {
  const [value, setValue] = useState(0); // 초기값을 0으로 설정
  return (
    <div>
      <p>
        현재 카운터 값은 <b>{value}</b> 입니다.
      </p>
      <button onClick={() => setValue(value + 1)}>+1</button>
      <button onClick={() => setValue(value - 1)}>-1</button>
    </div>
  );
};

연습문제(useState 사용)

  • 이름, 직책, 회사명, 회사주소, 이메일, 전화번호를 입력 받아 명함 형태로 출력하기
  • 코드 보기
    import React, { useState } from "react";
    
    const NameCard = (props) => {
        return (
            <>
                <h3>명함 정보 출력</h3>
                <p>이름 : {props.member.name}</p>
                <p>직책 : {props.member.position}</p>
                <p>회사 : {props.member.company}</p>
                <p>주소 : {props.member.addr}</p>
                <p>메일 : {props.member.eMail}</p>
                <p>전화 : {props.member.phone}</p>
            </>
        )
    }
    
    const UseState = () => {
    
        const [member, setMember] = useState({name:"", position:"",company:"",addr:"",eMail:"",phone:""});
        const [submit, setSubmit] = useState(false);
        const onChangeName = (e) => setMember({...member, name : e.target.value});
        const onChangePos  = (e) => setMember({...member, position : e.target.value});
        const onChangeComp = (e) => setMember({...member, company : e.target.value});
        const onChangeAddr = (e) => setMember({...member, addr : e.target.value});
        const onChangeMail = (e) => setMember({...member, eMail : e.target.value});
        const onChangePhone = (e) => setMember({...member, phone : e.target.value});
    
        const onSubmit = () => {
            setSubmit(true);
        }
       
    
        return (
            <>
                <h1>회원 정보 가입</h1>
                <input type="text"  placeholder="이름 입력" value={member.name} onChange={onChangeName} />
                <br />
                <input type="text" placeholder="직책 입력" value={member.position} onChange={onChangePos} />
                <br />
                <input type="text" placeholder="회사 입력" value={member.company} onChange={onChangeComp} />
                <br />
                <input type="text" placeholder="주소 입력" value={member.addr} onChange={onChangeAddr} />
                <br />
                <input type="email" placeholder="메일 입력" value={member.eMail} onChange={onChangeMail} />
                <br />
                <input type="tel" placeholder="폰 입력" value={member.phone} onChange={onChangePhone} />
                <br />
                <button onClick={onSubmit}>제출</button>
                {submit && <NameCard member={member}/>}
            </>
        );
    }
    export default UseState;

useEffect

useEffect는 React 함수형 컴포넌트에서 생명주기 메소드를 대체하는 React Hook 중 하나입니다. useEffect를 사용하면 컴포넌트가 렌더링될 때마다 특정 동작을 수행하거나, 특정 상태가 업데이트될 때마다 동작을 수행하도록 설정할 수 있습니다.

useEffect는 컴포넌트가 렌더링된 이후에 실행되며, 기본적으로 매 렌더링마다 실행됩니다. useEffect에 전달된 함수 안에서 상태(state)를 변경하면 컴포넌트가 다시 렌더링되고, useEffect는 렌더링 이후에 다시 실행됩니다.

useEffect는 첫 번째 인수로 콜백 함수를 받으며, 이 콜백 함수는 컴포넌트가 마운트되거나 언마운트될 때, 그리고 의존성 배열(deps)에 전달된 상태가 변경될 때마다 실행됩니다. 의존성 배열이 빈 배열이면, useEffect는 컴포넌트가 마운트될 때만 실행됩니다.

import {useState, useEffect} from "react";

const Info = () => {
    const[name, setName] = useState('');
    const[nickname, setNickname] = useState('');
    useEffect(() => {
        console.log("렌더링이 완료되었습니다.");
        console.log({name, nickname});
    });

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

    return (
        <div>
            <div>
                <input value={name} onChange={onChangeName} />
                <input value={nickname} onChange={onChangeNickname} />
            </div>
            <div>
                <div>
                    <b>이름 : </b> {name}
                </div>
                <div>
                    <b>닉네임 : </b> {nickname}
                </div>
            </div>
        </div>
    );
}

export default Info;
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

마운트 될 때만 실행하고 싶을 때

useEffect에서 설정한 함수를 컴포넌트가 화면에 맨 처음 렌더링될 때만 실행하고, 업데이트 될 때는 실행하지 않으려면 함수의 두번째 파라미터로 비어 있는 배열을 넣어 주면 됩니다.

useEffect(() => {
    console.log("마운트 될 때만 실행 됩니다.");
},[]);

특정 값이 업데이트 될 때만 실행하고 싶을 때

useEffect를 사용할 때, 특정 값이 변경될 때만 호출하고자 하는 경우, 클래스형 컴포넌트라면 아래와 같이 작성할 수 있습니다.

클래스 컴포넌트 사용하는 방법

componentDidUpdate(prevProps, prevState) {
	...
}

useEffect를 사용하는 방법

useEffect의 두 번째 파라미터로 전달되는 배열 안에 검사하고자 하는 값을 넣으면 됩니다.

배열안에는 useState를 통해 관리하가 있는 상태값을 넣어 주어도 되고, props로 전달받은 값을 넣어 주어도 됩니다.

useEffect(() => {
	console.log(name);
}, [name]);

시간 업데이트 하기

clearInterval()함수는 JavaScript에서 제공하는 함수로, setInterval()함수로 생성한 인터벌을 멈추게 합니다.

import React, { useState, useEffect } from 'react';

function Clock() {
  const [time, setTime] = useState(new Date());

  useEffect(() => {
    const interval = setInterval(() => {
      setTime(new Date());
    }, 1000);
     // MyComponent가 Unmount되면 interval을 해제합니다.
    return () => clearInterval(interval);
  }, []);

  return (
    <div>
      <h1>Current Time:</h1>
      <h2>{time.toLocaleTimeString()}</h2>
    </div>
  );
}

export default Clock;

useReducer

useReducer는 React Hooks 중 하나로서, 상태 관리를 위해 사용됩니다. useState와는 달리, useReducer는 복잡한 로직을 가진 상태들을 관리하는 데에 유용합니다.

useReducer는 두 개의 매개변수를 받습니다.

첫 번째 매개변수는 상태 업데이트를 처리하는 함수로서 "reducer" 라고 불리며, 두 번째 매개변수는 초기 상태입니다. 이 함수는 현재 상태와 액션(action) 객체를 받아서 새로운 상태를 반환합니다.

useReducer를 사용하면, dispatch라는 함수를 사용하여 상태를 업데이트 할 수 있습니다. dispatch함수는 액션 객체를 받고, 이 액션 객체는 상태를 업데이트할 때 필요한 정보를 포함하고 있습니다.

카운트 구현하기

state는 현재 상태, dispatch상태를 업데이트하는 함수입니다.

reducer는 상태를 계산하는 함수이며, initialState는 초기 상태입니다.

reducer 함수는 현재 상태와 액션을 인수로 받아 새로운 상태를 반환합니다. Counter
컴포넌트에서는 useReducer훅을 사용하여 상태와 dispatch 함수를 선언하고, 버튼 클릭 시 해당 액션을 전달합니다. 이렇게 하면 상태가 업데이트되고, 컴포넌트가 다시 렌더링됩니다.

useReducer를 사용했을 때 가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있습니다.

import { 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 = () => {
    const [state, dispatch] = useReducer(reducer, {value:0});
    return (
        <div>
            <p>
                현재 카운터 값은 <b>{state.value}</b> 입니다.
            </p>
            <button onClick={() => dispatch({type:"INCREMENT"})}>+1</button>
        </div>
    );
}
export default Counter;

useMemo

useMemoHook은 React 컴포넌트의 성능을 최적화하기 위해 사용됩니다. (이전에 계산된 값을 재사용할 수 있도록 해주는 Hook)

Memo는 “momoized”를 의미하며, 이전에 계산 한 값을 재사용한다는 의미를 가지고 있습니다.

React 컴포넌트에서 렌더링하는 동안, 컴포넌트의 상태나 props가 변경되면 해당 컴포넌트와 하위컴포넌트들이 다시 렌더링됩니다. 때로는, 이러한 불필요한 렌더링은 성능 문제를 야기할 수 있습니다.

일반적인 구현 방식

  • 일반적인 구현 방식으로 구현하는 경우 input 내용이 수정 될 때도 getAverage 함수가 호출됩니다.
  • Input 내용이 바뀔 때 마다, 즉 리렌더링 할 때마나 평균값을 계산하는 것은 불필요한 일 입니다.
import { useState } from "react";

const getAverage = numbers => {
    console.log("평균값 계산 중");
    if(numbers.length === 0) return 0;
    // 배열의 각 요소를 순회하며 callback 함수의 실행 값을 누적하여 하나의 결과값을 반환 합니다.
    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 = e => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    };
    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> {getAverage(list)}
            </div>
        </div>
    );
};
export default Average;

useMemo를 사용하는 방식

  • useMemo는 두 개의 인자를 받습니다. 첫 번째 인자는 캐시할 값을 계산하는 함수이고, 두 번째 인자는 의존성 배열(dependency array) 입니다.
  • 의존성 배열은 useMemo가 언제 캐시된 값을 다시 계산해야 하는지 결정하는 데 사용됩니다.
  • 의존성 배열에 있는 값이 변경되지 않으면 useMemo는 이전에 캐시된 값을 반환하고, 값이 변경되면 새로운 값을 계산합니다. (list배열이 변경될 때마다 getAverage함수가 다시 실행됩니다. list배열이 변경되지 않은 경우 이전에 계산된 값을 재사용합니다.)
import { useState, useMemo } from "react";

const getAverage = numbers => {
    console.log("평균값 계산 중");
    if(numbers.length === 0) return 0;
    // 배열의 각 요소를 순회하며 callback 함수의 실행 값을 누적하여 하나의 결과값을 반환 합니다.
    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 = e => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    };
		// 첫번째는 콜백 함수, 두번째 인자는 해당 배열
    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

useCallback은 useMemo와 상당히 비슷한 함수 입니다. 주로 렌더링 성능을 최적화해야 하는 상황에서 사용하며, useCallback을 사용하면 만들어 놨던 함수를 재사용 할 수 있습니다.

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

const getAverage = numbers => {
    console.log("평균값 계산 중");
    if(numbers.length === 0) return 0;
    // 배열의 각 요소를 순회하며 callback 함수의 실행 값을 누적하여 하나의 결과값을 반환 합니다.
    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('');
    }, [number, list]);

    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 Hook은 함수 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해 줍니다.

useRef를 사용하여 ref를 설정하면 useRef를 통해 만든 객체 안의 current 가리킵니다.

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

const getAverage = numbers => {
    console.log("평균값 계산 중" + numbers);
    if(numbers.length === 0) return 0;
    // 배열의 각 요소를 순회하며 callback 함수의 실행 값을 누적하여 하나의 결과값을 반환 합니다.
    const sum = numbers.reduce((a, b) => a + b);
    return sum / numbers.length;
};

const Average = () => {  // 컴포넌트 이름은 대문자
    const [list, setList] = useState([]);
    const [number, setNumber] = useState(0);
    const inputEl = useRef(null);

    const onChange = useCallback(e => {
        setNumber(e.target.value);
    }, []); // 빈 배열을 넣으면 처음 렌더링될 때만 함수 호출

    const onInsert = useCallback(() => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber(0);
        inputEl.current.focus();
    }, [number, list]);

    const avg = useMemo(() => getAverage(list), [list]);

    return (
        <div>
            <input value={number} onChange={onChange} ref={inputEl} />
            <button onClick={onInsert}>등록</button>
            <ul>
                {list.map((value, index) => <li key={index}>{value}</li>)}
            </ul>
            <div>
                <b>평균값 : </b> {avg}
            </div>
        </div>
    );
};
export default Average;
profile
FE 개발자🌱

0개의 댓글