본문 바로가기

React

[React] Hook(훅) - useState, useEffect, useRef, useReducer

728x90

 

 


훅(HOOK) 이란?

리액트 컴포넌트는 클래스형 컴포넌트(Class component)와 함수형 컴포넌트(Functional component)로 나뉜다

리액트 훅은 새로운 기능으로 React 16.8버전에 새로 추가된 기능으로 함수형태의 컴포넌트에서 사용되는 몇 가지 기술을 Hook이라고 부른다 (useState, userEffect 등)

리액트 훅은 함수형 컴포넌트가 클래스형 컴포넌트의 기능을 사용할 수 있도록 해주는 기능이다

 

Hook의 규칙

 

 1️⃣ 최상위에서만 Hook을 호출해야 한다 

 2️⃣ 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 안 된다

 3️⃣이 규칙을 따르면 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로

       Hook이 호출되는 것이 보장된다 

 4️⃣ 리액트 함수 컴포넌트에서만 Hook을 호출해야 한다

 

    보충 설명을 덧붙이자면  useState나 useEffect 같은 훅들이 여러 번 사용될 수 있는데,
    리액트는 이 훅들을 호출되는 순서대로 저장해 놓는 특징이 있다
    (👉매 랜더링마다 순서대로 훅 호출 )

    그렇기 때문에 조건문이나 반복문 안에서 훅을 쓰게 되면 훅은의 호출 순서가 꼬일 수 있기에
    항상 리액트 함수 최상위 레벨에서만 호출해야 한다는 것이다

 

 

반드시 알아야 할 기본 훅 ⭐

1️⃣ useState

useState ( 초기값 ) : 배열 반환 


✅ 첫번째 배열의 요소에는 현재값을, 두번째 요소는 상태를 변경하는 함수(setter) 를 반환한다

const [data, setData] = useState('초기값')

 


 

 2️⃣ useEffect

useEffect ( 실행시킬 콜백함수, 값에 따른 렌더링 지정 ) 

 

userEffect의 첫번째 매개변수는 실행시킬 콜백함수,

두번째 매개변수는 배열[ ]을 사용하여 특정값이 update 될 때만 실행시켜 줄 수 있다

 

 

useEffect( )는 컴포넌트의 라이프 사이클을 다루며

리액트 컴포넌트가 mount, mount이후, unmount 때마다 특정작업을 수행한

라이프 사이클

 

 

 

1️⃣ mount 이후컴포넌트가 마운트 됨

       ( 즉 컴포넌트의 첫번째 렌더링이 마치면 호출되는 메서드 )

       클래스형 componentDidMount( ) 대체

 

함수형 훅

useEffect( () => {
	console.log(`렌더링완료`);
});

    ⚠️ 오늘의  오 마이 미스테이크!

         useEffect   =  ( )  =>  {           

                   // 화살표 함수처럼 작성하는 게 익숙해져서 실수함(이러면 난리남...알고 싶지 않았음 나도 ㅎ)

                  console.log(`렌더링 완료`);

          };


 

 

만약 mount 이후에 업데이트 될 때는 실행되지 않으려면, 두번째 매개변수 배열를 주면 된다

 

useEffect( () => {
	console.log(`처음만 실행됩니다`);
}, []);​

 

 

 

2️⃣ update 이후 - 특정값에 의해 컴포넌트가 업데이트 되고 난 후 발생

       클래스형 componentDidUpdate( ) 대체

 

함수형 훅

const HookEffect = () => {
    //useState
    const[name, setName] = useState('');
    const handleName = (e) => {
        setName( e.target.value );
    }

    //특정값이 업데이트 될 때만 실행해주려면 두번째 매개변수에 값을 state값을 지정합니다
    useEffect( () => {
        console.log(`name이 업데이트 시 실행됩니다`)
    }, [name]);

    return (
        <div>
            
            이름:<input type="text" onChange={handleName}/><br/>
            이름:{name}
        </div>
    )
}

export default HookEffect;

 

 

 

 3️⃣ unmount직전컴포넌트가 화면에서 사라지기 직전 호출

        클래스형 componentWillUnMount() 대체

 

함수형 훅

const HookEffect = () => {
    //useState
    const[name, setName] = useState('');
    const handleName = (e) => {
        setName( e.target.value );
    }
    
	//useEffect    
    useEffect( () => {
        console.log(`name이 업데이트 시 실행됩니다`)
        
        //unmount이후 실행됩니다.
        return () => {
            console.log(`unmount에 실행됩니다.`);
        }
    }, [name]);

    return (
        <div>
            이름:<input type="text" onChange={handleName}/><br/>
            이름:{name}
        </div>
    )
}

export default HookEffect;

 


 한번에 보는 useEffect  😉

 


 

3️⃣ 특정 태그에 이름달기 -  useRef ( )

 

const 사용할이름  =  useRef (null);

 

이벤트를 사용하다 보면 특정 태그에 접근해서 핸들링 하는 경우가 생긴다

arrow function에 event 매개변수를 이용해서, 자신 태그에는 접근할 수 있지만 다른태그는 핸들링 하기가 어렵다

이런 경우 useRef( ) 훅을 이용해서 특정태그에 이름을 지정하고 핸들링 할 수 있다

 

const HookRef = () => {
    //useState
    const [form, setForm] = useState({data: '', result: '' });
    //useRef
    const inputTag = useRef(null);
    //인풋핸들링
    const handleChange = (e) => {
        setForm( {data :e.target.value} ); //인풋값이 변할때마다 data변수 변화
    }
    
    //버튼핸들링 - 클릭시 state는 변경하고, input태그에 포커스
    const handleClick = (e) => {
        setForm( {data: '', result: form.data})
        //useRef의사용 - current로 ref의 현재태그에 접근가능
        inputTag.current.focus()
    }

    return (
        <div>
            할일: <input type="text" value={form.data} onChange={handleChange} ref={inputTag} />
            <button type="button" onClick={handleClick}>등록하기</button>

            <br/>
            결과: {form.result}

        </div>
    )
}

export default HookRef;

 


 

부가적으로 알면 좋은 훅 

useReducer

   useState의 사용을 외부에서 사용할 수 있게 해주는 훅( state의 변경을 외부에서 제어 )

  const [현재값, 리듀서를 업데이트할 수 있는 함수] = useReducer(외부에서 사용할 리듀서함수, 리듀서의 초기값)
 

 

 


Hook(useEffect, useState, useRef) 으로 다음의 조건을 충족시켜보렴 👩‍💻

 

첫 번째 실습

   1️⃣ 컴포넌트가 마운트된 이후 한번만 id태그에 포커스를 주기

   2️⃣ id, pw는 state로 관리한다

           로그인 버튼 클릭시 공백이라면 공백인 태그에 포커스를 주기

           로그인 버튼 클릭시 공백이 아니라면 경고창으로 id와 pw를 출력

 
    

[ 전체 코드 ]

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


const HookEffect_again = () => {


    /*
        1.컴포넌트가 마운트된 이후 한번만 id태그에 포커스를 준다

        2.id, pw는 state로 관리한다
        로그인 버튼 클릭시 공백이라면 공백인 태그에 포커스를 주세요
        로그인 버튼 클릭시 공백이 아니라면 경고창으로 id와 pw를 출력해주세요
    */

    //컴포넌트가 마운트된 이후 딱 한 번 id태그에 포커스 주기
    useEffect (() => {
        inputId.current.focus();
    },[]);

    const inputId = useRef(null);
    const inputPw = useRef(null);


    //id, pw state로 관리
    const [data, setData] = useState({id:'', pw:''});

    //id, pw창에 값이 입력되었을 때, 사용자가 입력한 값을 state에 저장 => 태그의 value에 값을 넣어야 화면 출력
    const handleChange = (e) => {
        setData({...data, [e.target.name]: e.target.value});
        // console.log(e.target.value);
        // console.log(setData);
    }

    //로그인 버튼 클릭
    const handleClick =() =>{
       
        if(inputId.current.value === ''){
            inputId.current.focus();
        }else if(inputPw.current.value === ''){
            inputPw.current.focus();
        } else{
            alert(`아이디: ${data.id}, 비밀번호: ${data.pw}`);
        }
        
    }


    return (
        <div>
            <h3>훅 실습</h3>
            <input type="text" name="id" placeholder="아이디" value={data.id} onChange={handleChange} ref={inputId} /><br />
            <input type="password" name="pw" placeholder="비밀번호" value={data.pw} onChange={handleChange} ref={inputPw} /><br />
            <button onClick={handleClick}>로그인</button>
        </div>
    )
}

export default HookEffect_again;

 

 

두 번째 실습

    실습(할 일 목록 만들기)
     1️⃣ state { todo : ' ', list : [ ] }로 관리하기
     2️⃣ 할 일 목록을 작성하고, 클릭시 list에 인풋의 입력값을 추가하고 map을 통해서 화면을 그린다
     3️⃣ 등록된 이후에는 ref를 활용해서 input태그에 포커싱을 준다 

 

import { useRef, useState } from "react";

const HookQ2_mine = () => {

        // 1. state {todo:'', list:[]}로 관리하기
        const [data,setData] = useState({todo:'', list:[]});

        const inputId = useRef(null);


        //2. 할 일 목록을 작성하고 (이벤트 객체 이용)
        const handleChange = (e) => {
            setData({...data, [e.target.name]: e.target.value});
        }
        
        // 클릭시 list에 인풋의 입력값을 추가하고 map을 통해서 화면을 그린다
        const handleClick = () => {
            let newList3 = data.list.concat(data.todo);
            setData({...data, ['todo']:'' , ['list']:newList3});
            inputId.current.focus(); // 등록된 이후 포커싱    
        }
        
        //화면에 출력
        const newData = data.list.map((item, index) => 
        <li key={index}>
            {item}
        </li>
        )
        
        //엔터키 처리
        const handlePress =(e) =>{
            if(e.keyCode == 13){
                handleClick();
            }
        }

    return(
        <div>
            <h3>ref로 할 일 목록 만들기</h3>
            <input type="text" name="todo" onChange={handleChange} onKeyDown={handlePress}  placeholder="할 일 목록" value={data.todo} ref={inputId}/>
            <button onClick = {handleClick} >등록하기</button>
            <ul>
                {newData}
            </ul>
        </div>
    )

    }
    
export default HookQ2_mine;