React Hook (useState, useEffect, useRef)

limchard·2023년 12월 12일
0

React

목록 보기
4/7

Hook

React Hook 이란?

리액트 훅은 리액트 클래스형 컴포넌트에서 이용하던 코드를 작성할 필요없이 함수형 컴포넌트에서 다양한 기능알 사용할 수 있게 만들어준 라이브러리라고 할 수 있는데 React 16.8 버전에 새로 추가된 기능이다. 이는 함수형 컴포넌트에 맞게 만들어진 것으로 함수형 컴포넌에서만 사용 가능하다.

Hook 규칙

최상위에서만 Hook을 호출해야 한다.

  • 반복문이나 조건문 호은 중첩된 함수 내에서 Hook을 호출하면 안된다.
  • 리액트 혹은 호출되는 순서에 의존하기 때문에 조건문이나 반복문 안에서 실행하게 될 경우 해당 부분을 건너뛰는 일이 발생할 수 있기 때문에 순서가 꼬여 버그가 발생할 수 있다.
  • 그렇기 때문에 이 규칙을 따르면 useState 와 useEffect가 여러번 호출되는 경우에도 Hook의 상태를 올바르게 유지할 수 있게 된다.

리액트 함수 내에서만 Hook을 호출해야 한다.

  • Hook은 일반적인 js 함수에서는 호출하면 안된다.
  • 함수형 컴포넌트나 costum hook에서는 호출 가능하다.

useState

useState는 상태를 관리하는 훅으로 다음과 같은 특징을 가진다.

  • 함수형 컴포넌트 안에 state를 추가하여 사용한다.
  • 현재 상태를 나타내는 state값과 이 상태를 변경하는 setState 값을 한쌍으로 제공한다.
  • state는 초기값을 설정할 수 있으며, 초기값은 첫 렌더링 때 한번 사용된다.
  • state는 객체일 필요 없이 문자열, 숫자 boolean, 배열, null, 객체 등의 여러가지 다양한 값을 넣을 수 있다.
  • 반환값을 확인해보면 Object 로 넘어온다.
import React, { useState } from 'react';
import cat from '../image/3.jpg'

function FourthApp(props) {

    const [name,setName]=useState('뽀로로');
    const [age,setAge]=useState('22');

    return (
        <div>
            <h3 className='alert alert-danger'>FourthApp 입니다.
                <img className='rounded-circle' src={cat} style={{width:'200px',marginLeft:'50px'}}/>
            </h3>
            <br/>
            <b style={{fontSize:'30px'}}>이름:{name} &nbsp;&nbsp; <span style={{marginLeft:'20px'}}>나이: {age}</span></b>
            <br/>
            <button type='button' className='btn btn-info' onClick={()=>{
                setName("리처드");
                setAge(25);
            }}>값변경</button>
            <button type='button' className='btn btn-danger' style={{marginLeft:"20px"}}
            onClick={()=>{
                setName("");
                setAge('');
            }}>초기화</button>
        </div>
    );
}

export default FourthApp;


useEffect

useEffect는 기존 클래형 컴포넌트에서 사용했던 componentDidMount, componentDidUpdate, componentWillUnmount를 하나의 API 로 통합한 것으로 다음과 같은 특징을 가진다.

  • 렌더링 오류가 날 경우 그 이유를 찾을 때 사용하는 경욱가 많다.
  • 값 입력시 계쏘해서 렌더링되며 호출될 수도 있고, 생성자처럼 딱 한번 호출되기도 한다.
  • useEffect는 기본적으로 useEffect(function, deps)의 형태를 사용한다.
  • function에는 실행시킬 함수를 넣고 deps에는 의존성 배열을 담는다.
  • 의존성 배열에 어떤 것이 담기느냐에 따라 부수적인 효과 함수가 실행된다.
  • 가장 먼저 의존성 배열 없이 useEffect를 실행시키게 되면 페이지가 렌더링 될 때마다 데이터를 불러온다.
const [name,setName]=useState('뽀로로');
const [addr,setAddr]=useState('남극');

    // 랜더링 오류날 경우 그 이유를 찾을 때 사용하는 경우가 많다.
    // useEffect : 값 입력시 계속해서 랜더링되며 호출될 수도 있고, 생성자처럼 딱 한번 호출 되기도 한다.
    useEffect(()=>{
       console.log("state변수가 값 변경 시 마다 호출이 된다.");
    });

  • 두번째로 의존성 배열에 빈배열을 담아주게 될 경우에는 첫 렌더링 시에만 함수를 실행한다.
useEffect(()=>{
        console.log("처음랜더링 시 한번만 호출한다. 마치 생성자처럼..");
    },[]);

  • 마지막으로 의존성 배열에 state값이나 props로 상속받은 데이터값 등일 담아주게 되면 해당값이 변할때마다 함수를 실행한다.
useEffect(()=>{
        console.log("이름 변경시에만 호출하고 싶어");
    },[name]);

useEffect 예제 원문

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

function OneApp(props) {

    const [name,setName]=useState('뽀로로');
    const [addr,setAddr]=useState('남극');

    // 랜더링 오류날 경우 그 이유를 찾을 때 사용하는 경우가 많다.
    // useEffect : 값 입력시 계속해서 랜더링되며 호출될 수도 있고, 생성자처럼 딱 한번 호출 되기도 한다.
    useEffect(()=>{
       console.log("state변수가 값 변경 시 마다 호출이 된다.");
    });
    // 오류가 나면..
    useEffect(()=>{
        console.log("처음랜더링 시 한번만 호출한다. 마치 생성자처럼..");
    },[]);

    useEffect(()=>{
        console.log("이름 변경시에만 호출하고 싶어");
    },[name]);

    return (
        <div>
            <h3 className={'alert alert-danger'}>OneApp입니다._useEffect</h3>
            <h3>이름: <input type={"text"} className={'form form-control'} defaultValue={name} style={{width:'200px',display:'inline-flex'}}
            onChange={(e)=>{
                setName(e.target.value);
            }}/></h3>
            <h3>주소: <input type={"text"} className={'form form-control'} defaultValue={addr} style={{width:'200px',display:'inline-flex'}}
            onChange={(e)=>{
                setAddr(e.target.value);
            }}/></h3>
            <hr/>
            <h2>이름: {name} </h2>
            <h2>주소: {addr} </h2>
        </div>
    );
}

export default OneApp;

useRef

useRef는 특정 DOM에 접근하여 DOM 조작을 가능하게 하는 훅이다. 리액트 프로젝트에서도 특정 요소를 선택해야하는 상황이 생길 수 있는데 이러한 상황에서 useRef 함수를 사용할 수 있게 된다.

  • useRef: 변수를 관리하는 기능
import React, {useRef, useState} from 'react';

function FourApp(props) {

    // useRef : 변수를 관리하는 기능.
    // useState 와의 차이점: 값이 변경되어도 다시 렌더링 하지 않음.

    // 일반 state 변수
    const [count,setCount]=useState(0);

    // ref 선언하는 법
    const countRef=useRef(0);

    // state 변수 증가하는 함수
    const stateIncre=()=>{
        setCount(count+1);
    }

    // ref변수 증가하는 함수
    const refIncrer=()=>{
        countRef.current=countRef.current+1; // ref변수.current (정해진 변수.) 무조건 current를 써야 한다
        // 1 증가해서 다시 current
        console.log("countRef: "+countRef.current);
    }

    return (
        <div>
            <h3 className={'alert alert-primary'}>FourApp입니다._useRef</h3>
            <button type={"button"} className={'btn btn-outline-danger'} onClick={stateIncre}>state 변수 증가</button>
            <div className={'number'}>{count}</div>
            <button type={"button"} className={'btn btn-outline-warning'} onClick={refIncrer}>ref변수 증가</button>
            <div className={'number'}>{countRef.current}</div>
        </div>
    );
}

export default FourApp;

useRef 예제

  • 수정된 값들을 클릭할 때 한번에 보여주기 위해 사용.
import React, {useRef, useState} from 'react';

function FiveApp(props) {

    const [result,setResult]=useState();
    const nameRef=useRef();
    const javaRef=useRef();
    const reactRef=useRef();
    const springRef=useRef();

    const buttonResult=()=>{
        // 데이터 읽어오기
        let name=nameRef.current.value;
        let java=Number(javaRef.current.value);
        let react=Number(reactRef.current.value);
        let spring=Number(springRef.current.value);

        let tot=java+react+spring;
        let avg=(tot/3).toFixed(2); // 소숫점..

        let r=`[결과확인] 
        이름 : ${name} 
        자바점수: ${java} 
        리액트점수: ${react}
        스프링점수: ${spring} 
        총점: ${tot} 
        평균: ${avg}`;

        setResult(r);
    }

    return (
        <div>
            <h3 className={'alert alert-warning'}>FiveApp입니다._useRef 예제</h3>
            <h4>이름입력 : <input type={"text"} ref={nameRef}/></h4>
            <h4>자바점수 : <input type={"text"} ref={javaRef}/></h4>
            <h4>리액트점수 : <input type={"text"} ref={reactRef}/></h4>
            <h4>스프링점수 : <input type={"text"} ref={springRef}/></h4>
            <button type={"button"} className={'btn btn-dark'} onClick={buttonResult}>결과확인</button>
            <h2 className={'alert alert-danger'} style={{whiteSpace:'pre-wrap'}}>{result}</h2>
        </div>
    );
}

export default FiveApp;


useMemo, useCallback

useMemo와 useCallback은 모두 메모이제이션과 관련이 있어 리액트를 사용하며 성능 개선을 위해 많이 사용된다. 여기서 메모이제이션이라는 것은 기존에 수행한 연산의 결과 값을 어딘가에 저장해두었다가 동일한 입력이 들어오면 재활용하겠다는 프로그래밍 기법이라고 할 수 있다. 그렇기 때문에 useMemo나 useCallback 훅을 적절하게 사용하면 중복된 연산을 피할 수 있어 애플리케이션의 성능을 최적화할 수 있다는 장점이 있다.
useMemo와 useCallback이 비슷한 동작을 하긴 하지만 두가지의 차이도 존재한다. useMemo의 경우는 메모이제이션된 값을 반환하는 훅이고, useCallback은 메모제이션 된 함수를 반환한다는 특징이 있다.


useState 를 객체변수로 처리하기

  • 값을 하나하나 가져오는 것이 아니라 한번에 가져온다.
import React, {useState} from 'react';

function TwoApp(props) {

    // 객체처리변수
    const [inputs,setInputs]=useState({
        irum:'뽀로로',
        addr:'남극',
        age:20
    });

    // 3개의 공통함수
    const changeData=(e)=>{
        console.log("name: "+e.target.name);
        console.log("value: "+e.target.value);

        // name.value값 얻기
        // e.target이 가지고 있는 객체 한번에 변경하기
        const {name,value}=e.target;

        // 변경
        setInputs({
            ...inputs, // 기존의 멤버값을 유지해준다.
            [name]:value // 해당값만 나오고 나머지는 사라진다. 해결은 펼침연산자!
        })
    }

    return (
        <div>
            <h3 className={'alert alert-secondary'}>TwoApp입니다._state 를 객체변수로 받아서 테스트</h3>
            <h3>이름 : <input type={"text"} className={'form form-control'} name={'irum'} defaultValue={inputs.irum}
                            onChange={changeData}/></h3>
            <h3>주소 : <input type={"text"} className={'form form-control'} name={'addr'} defaultValue={inputs.addr}
                            onChange={changeData}/></h3>
            <h3>나이 : <input type={"text"} className={'form form-control'} name={'age'} defaultValue={inputs.age}
                            onChange={changeData}/></h3>
            <hr/>
            <h2>이름: {inputs.irum}, {inputs["irum"]}</h2>
            <h2>주소: {inputs.addr}, {inputs["addr"]}</h2>
            <h2>니이: {inputs.age}, {inputs["age"]}</h2>

        </div>
    );
}

export default TwoApp;

import React, {useState} from 'react';

function ThreeApp(props) {

    const [boards,setBoards]=useState([
        {
            no:1,
            writer:'뽀로로',
            subject:'영환이 보쏘',
            photo:'s1'
        },
        {
            no:2,
            writer:'피카츄',
            subject:'백만볼트',
            photo:'s2'
        },
        {
            no:3,
            writer:'라이츄',
            subject:'천만볼트',
            photo:'s3'
        },
        {
            no:4,
            writer:'토게피',
            subject:'몸통박치기',
            photo:'s4'
        },
        {
            no:5,
            writer:'마자용',
            subject:'맞아?',
            photo:'s5'
        }
    ]);

    return (
        <div style={{marginLeft:'100px'}}>
            <h3 className={'alert alert-info'}>ThreeApp입니다._객체배열 컴포넌트 배열안에 출력</h3>
            <table className={'table table-bordered'} style={{width:'600px'}}>
                <caption align={'top'}><b>배열객체출력</b></caption>
                <thead>
                    <tr>
                        <th width={'60'}>번호</th>
                        <th width={'100'}>사진</th>
                        <th width={'260'}>제목</th>
                        <th width={'100'}>작성자</th>
                    </tr>
                </thead>
                <tbody>
                {
                    boards.map((row,index)=>(<tr>
                        <td>{row.no}</td>
                        <td><img src={`../image2/${row.photo}.JPG`} style={{width:'50px'}}/></td>
                        <td>{row.subject}</td>
                        <td>{row.writer}</td>
                    </tr>))
                }
                </tbody>
            </table>

        </div>
    );
}

export default ThreeApp;

profile
java를 잡아...... 하... 이게 맞나...

0개의 댓글