본문 바로가기
front-end/리액트

[리액트] hook :useState, useEffect, useContext

by CodeMango 2023. 3. 30.

1. useState

최종목적은 변수관리입니다. 

변수 사용할 때 무조건 사용해야 합니다.

예제1)

function App() {
//const는 외부의 접근 차단하기 위함 (보호) -> 컴포넌트 안에서만 접근이 가능하다.
  const [state, setState] = useState('click');
       //getter, setter             초기값
   
  //state = "새로운 값"; // 상수에 대입 불가 -> const변수라서 !! 
  //setState("새로운 값"); //다시 초기화 불가능 -> const변수라서 !! 

  //getter만 사용할 수 있는것임.
  console.log(state);

  function btnClick(){
    setState('클릭했음');
  }

  btnClick()으로 하면 Too may rerenders라는 오류남
  = 리액트는 계속해서 동작하므로 한번만 동작이 되게 해줘야 오류가 안남
   btnClick()라고 적으면 계속해서 동작이 되므로 오류나니까 한번만 동작되게 하기 위해서는
   방법1. 뒤에 있는 괄호 빼기{ btnClick }  방법2. { ()=>btnClick() } 라고 쓰기.

  return (
    <div>
      {/* <button onClick={ () =>{setState('clicked')} }>{state}</button>  */}
      {/* <button onClick={ btnClick() }>{state}</button>   */}

  방법1    {/* <button onClick={ btnClick }>{state}</button>   */}
  방법2    <button onClick={ ()=>btnClick() }>{state}</button>  
    </div>
  );
}

Too many renders 오류 해결방법

->  btnClick() 이라고 쓰면 Too may rerenders라는 오류가 납니다.
리액트에서는 함수가 계속해서 동작이 돼서 오류가 나는 것이므로 한번만 동작이 되게 해줘야 오류가 안 납니다.
한번만 동작되게 하기 위해서는  두가지 방법이 있습니다.

방법1. 뒤에 있는 괄호 빼고 { btnClick }  만 쓰기

<button onClick={ btnClick }>{state}</button>

방법2. { ()=>btnClick() } 라고 쓰기.

 <button onClick={ ()=>btnClick() }>{state}</button>

예제2) input, select

~~input, select~~
function App(){
  const [text, setText] = useState('초기문자열');
  const [val, setVal] = useState('pear');
  
  function textChange(e){
     console.log(e.target.value);  //이렇게 하면 초기문자열이라는 글자는 절대 바뀌지 않고 콘솔만 바뀐다.
     setText(e.target.value); //이렇게 하면 setText를 통해서 test값이 바뀌면서 적용된다.
  }

  function btnClick(){
     alert(text);  //text가 getter
   }

  function selectChange(e) {  //select 요소의 값이 변경될 때마다 호출되어 선택된 과일 값을 val 상태 값으로 설정
    setVal(e.target.value);
  }

  function btnSelect() {  //버튼을 클릭했을 때 val 상태 값을 알림창으로 띄움
    alert(val);
  }

  //textChange 함수 호출 
  //on다음에 무조건 대문자 적어야 오류 안남. onChange, onClick
  return (
    <div>
      <input value={text} onChange={textChange} /> 
      <input type='button' value="입력" onClick={btnClick} />

      {/* 1. 함수로 안 적고 따로 적기 / 2. 함수로 빼서 적기 */}
{/* 1. <select value={val} onChange= {(e)=>setVal(e.target.value)}>   */}
{/* 2 */}
      <select value={val} onChange= {selectChange}> 
        <option value="apple">사과</option>
        <option value="pear">배</option>
        <option value="banana">바나나</option>
      </select>
     
      <button onClick={btnSelect}>선택</button> 
    </div>
  );
}

 

위 코드에서 나타난 개념으로 함수 호출을 하지 않고 쓰는 방법과 호출해서 쓰는 방법이 있습니다.  

 

1) 함수 호출 안 하고 코드 안에 작성하기 (es6문법)

<select value={val} onChange= {(e)=>setVal(e.target.value)}>   */}

 

2) 함수 호출하기

  function selectChange(e) { 
    setVal(e.target.value);
  }

 <select value={val} onChange= {selectChange}>

예제3) radio, checkbox

//~~radio,checkbox~~
function App(){
  return (    //컴포넌트 호출
    <div>
      <RadioInput />
      <CheckBoxInput />
    </div>
  )
}
function RadioInput(){
  const [rvalue, setRvalue] = useState('red');

  function radioChange(e){ 
    setRvalue(e.target.value);
  }
  return (
    <div className="ri">
      <p>color: <b>{rvalue}</b></p>

{/* 
checked 속성은 라디오 버튼 요소의 선택 여부를 결정합니다. checked 속성에는 true 또는 false 값을 설정할 수 있습니다. 
true가 설정되면 라디오 버튼이 선택된 상태가 되고, false가 설정되면 라디오 버튼이 선택되지 않은 상태가 됩니다.

위 코드에서는 라디오 버튼 요소의 선택 여부를 결정하는데 rvalue === 'red'와 같이 비교 연산자를 이용하여 
true 또는 false 값을 반환하도록 설정되어 있습니다. 이를 통해 rvalue 값이 'red'일 때는 첫 번째 라디오 버튼 요소가 선택된 상태가 되고, 
rvalue 값이 'green'일 때는 두 번째 라디오 버튼 요소가 선택된 상태가 되며, 
rvalue 값이 'blue'일 때는 세 번째 라디오 버튼 요소가 선택된 상태가 되도록 구현되어 있습니다. */}


    {/* selected 설정이 안되므로 value값을 잡아줘야 한다. 
    rvalue가 red면 checked가 true가 돼서 체크됨
    스프링에서는 name으로 묶었음*/}
      <input type="radio" value="red" onChange={radioChange} checked={rvalue === 'red'} />red
      <input type="radio" value="green" onChange={radioChange} checked={rvalue === 'green'}/>green
      <input type="radio" value="blue" onChange={radioChange} checked={rvalue === 'blue'} />blue
    </div>
  )
}

function CheckBoxInput(){
  //체크박스는 체크가 됐다/안됐다만 있으니까 체크 안됐을 때는 boolean 형태인 false로 체크
  const [checkval, setCheckval] = useState(false); //체크가 안됐을 때는 false

  function checkChange(e){
    // setCheckval(e.target.value); //체크 해제가 안됨
    setCheckval(!checkval); // 들어오는값 반대값이라고 해놓으면 체크 해제됨
    alert(!checkval);
    //!checkval가 최종으로 들어오는 값이 된다 
    //독서에 체크하면 true, 체크해제하면 false들어옴
  }

  return (
    <div>
      <input type='checkbox' onChange={checkChange} checked={checkval} />독서

    </div>
  )
}

export default App;

2. useEffect

1. 컴포넌트 초기화시 작업 수행

useEffect 함수를 사용하여 컴포넌트가 처음 렌더링될 때 한 번만 작업을 수행할 수 있습니다.
이때 useEffect 함수의 의존성 배열에 빈 배열([])을 전달하면 됩니다.
이러한 경우에는 컴포넌트가 마운트될 때 작업이 수행됩니다.

2.렌더링 완료 후 작업 수행
useEffect 함수를 사용하여 컴포넌트가 렌더링이 완료된 후에 작업을 수행할 수 있습니다.
이때 useEffect 함수의 의존성 배열에 어떤 값을도 전달하지 않으면 됩니다. 이러한 경우에는 컴포넌트가 렌더링된 후 작업이 수행됩니다.

3.특정 변수의 값이 변경되었을 때 작업 수행
useEffect 함수를 사용하여 특정 변수의 값이 변경될 때마다 작업을 수행할 수 있습니다.
이때 useEffect 함수의 의존성 배열에 해당 변수를 전달하면 됩니다. 이러한 경우에는 해당 변수의 값이 변경될 때 작업이 수행됩니다.
위 세 가지 경우에는 모두 useEffect 함수를 사용하여 컴포넌트의 상태를 관리하고 특정 작업을 수행할 수 있습니다.
단, 각 경우에 따라 의존성 배열의 값이 다르게 설정되어야 하므로, 의존성 배열을 적절하게 설정하는 것이 중요합니다.

4.mount/unmounts/update class에서 사용합니다. function타입이 아니라 class로 만들면 그때 사용합니다.

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

function App() {
  const [count, setCount] = useState(0);
  const [number, setNumber] = useState(10);

  function counter(){
    setCount(count + 1);
  } 

  function numberPlus(){
    setNumber(number + 1);
  }

  // rendering 완료될 때마다 발생
  // useEffect(()=>{
  //   console.log('useEffect 실행됨 ', count);
  // });

  // 이 component가 처음 실행시에 한번만 실행
  // useEffect(()=>{
  //   console.log('useEffect 실행됨 ', count);
  // }, []);

  // 특정의 변수가 변경되었을 시에 발생하고 싶은 경우
  useEffect(()=>{
       console.log('useEffect 실행됨 ', count);
  }, [count, number]);

  return (
    <div>
      <h3>카운터: {count}</h3>
      <button onClick={counter}>+</button>

      <h3>넘버: {number}</h3>
      <button onClick={numberPlus}>++</button>
    </div>
  );
}

export default App;

useEffect는 React 컴포넌트에서 side effect를 수행하는 훅(hook)입니다.

side effect란, 컴포넌트 내부에서 상태 변경 이외의 다른 일을 수행하는 것을 의미합니다. 

예를 들어, 외부 API 호출, DOM 조작, 타이머 설정 등이 해당됩니다.

useEffect는 컴포넌트가 렌더링 될 때마다 실행되기 때문에, 함수 컴포넌트에서 side effect를 수행할 때 사용됩니다.

위 코드에서는 useEffect를 세 가지 방식으로 사용하고 있습니다.

1. 모든 렌더링마다 실행

useEffect(() => {
  console.log('useEffect 실행됨 ', count);
});


useEffect의 첫 번째 인자로 전달된 함수는 컴포넌트가 렌더링될 때마다 실행됩니다. 이렇게 사용하면 매번 실행되기 때문에, 이 함수 안에서 상태를 변경하면 무한 루프에 빠질 수 있으므로 주의해야 합니다.

2. 첫 렌더링에만 실행

useEffect(() => {
  console.log('useEffect 실행됨 ', count);
}, []);


useEffect의 두 번째 인자로 빈 배열([])을 전달하면, 컴포넌트가 처음 렌더링될 때 한 번만 실행됩니다. 이렇게 사용하면, 컴포넌트가 처음 렌더링될 때만 수행해야 하는 작업을 처리할 수 있습니다.


빈 배열을 전달하면, useEffect 함수 내부에서 관찰할 상태나 props가 없다는 것을 의미합니다. 따라서, 컴포넌트가 처음 렌더링될 때만 useEffect 함수가 실행되고, 그 이후에는 관찰할 상태나 props가 없기 때문에 다시 실행되지 않습니다.

만약 빈 배열 대신 상태나 props를 전달하면, 해당 상태나 props가 변경될 때마다 useEffect 함수가 실행됩니다.

예를 들어, 다음과 같은 코드가 있다면,

useEffect(() => {
  console.log('count 상태가 변경될 때마다 실행됩니다.');
}, [count]);

count 상태가 변경될 때마다 console.log 메소드가 실행되며, 그렇지 않은 경우에는 실행되지 않습니다.

3.특정 상태 값이 변경될 때만 실행

useEffect(() => {
  console.log('useEffect 실행됨 ', count);
}, [count, number]);


useEffect의 두 번째 인자로 배열을 전달하면, 배열 안에 포함된 상태 값이 변경될 때만 함수가 실행됩니다. 이렇게 사용하면, 특정 상태 값이 변경될 때만 수행해야 하는 작업을 처리할 수 있습니다.

위 코드에서는 useEffect를 이용하여, 컴포넌트 렌더링이 발생할 때마다 console.log를 실행하고 있습니다.

첫 번째 useEffect는 모든 렌더링마다 실행되므로 무한 루프에 빠지게 됩니다.

두 번째 useEffect는 처음 렌더링할 때만 실행되므로 console.log가 한 번 출력됩니다.

세 번째 useEffect는 count나 number 상태가 변경될 때마다 실행되므로, 각 상태가 변경될 때마다 console.log가 실행됩니다.

 

useEffect를 활용한 예시

한 번 클릭했을 때 조회수를 올려주고, 그 다음에 동일 아이디로 클릭했을 때는 다시는 조회수를 올리지 않는 방법

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

function App() {
  // count 상태를 정의하고, 초기값을 0으로 설정합니다.
  const [count, setCount] = useState(0);

  // clickedIds 상태를 정의하고, 초기값을 빈 배열로 설정합니다.
  const [clickedIds, setClickedIds] = useState([]);

  // 컴포넌트가 처음 렌더링될 때 실행됩니다.
  // localStorage에서 clickedIds 값을 가져와서 clickedIds 상태에 저장합니다.
  useEffect(() => {
    const clickedIdsString = localStorage.getItem('clickedIds');
    if (clickedIdsString) {
      setClickedIds(JSON.parse(clickedIdsString));
    }
  }, []);

  // clickedIds 상태가 변경될 때마다 실행됩니다.
  // clickedIds 값을 localStorage에 저장합니다.
  useEffect(() => {
    localStorage.setItem('clickedIds', JSON.stringify(clickedIds));
  }, [clickedIds]);

  // 조회 버튼을 클릭할 때 실행됩니다.
  const handleClick = () => {
    const id = 'user_id_123'; // 조회수를 올릴 아이디
    if (!clickedIds.includes(id)) { // 이전에 클릭한 적이 없으면
      setCount(count + 1); // 조회수를 1 증가시키고
      setClickedIds([...clickedIds, id]); // 클릭한 아이디를 기록합니다.
    }
  };

  // 조회수와 버튼을 렌더링합니다.
  return (
    <div>
      <p>조회수: {count}</p>
      <button onClick={handleClick}>조회</button>
    </div>
  );
}

export default App;

댓글