[React] useState, useEffect, useRef정리 및 실습코드

지구·2021년 12월 6일
0

리액트마스터

목록 보기
1/1
post-thumbnail

목차

**데이터 - state에 저장하기

  • useState
    Hook 사용 이유
    useEffect
    useRef**

I. 데이터를 state에 저장하기

왜 state를 써야하나?

변수가 변경될 때 자동으로 HTML이 리렌더링되게 하려면, 변수에 말고 state에 데이터 바인딩해야한다.

예를 들어 D-day계산기에서 처럼 입력한 숫자에 따라 표시되는 날짜가 바뀌게 하고싶다

→ 수정될 때마다 스무스하게 변동사항이 반영되게하고싶다

→ 입력한 숫자, 표시되는 날짜 모두 state로 놓고 사용해야한다.

그렇다고 모든 데이터를 state로 바인딩하냐? - 바뀌지 않을 값들은 굳이 하지 않는다.

그럼 state 변경 어떻게 하나? ⇒ useState!

state는 변수처럼 그냥 재할당해서 변경하면 안됨. 특별한 방법을 써야함! 변수처럼 쓴다면 state쓰는 의미가 없음..

특별한방법? useState함수 사용하기! (class컴포넌트라면 setState...)

  • useState로 만든 set어쩌고 함수에 파라미터를 넣어서 호출하면 전달받은 파라미터로 값이 바뀌고 컴포넌트가 리리렌더링된다!

1. useState : state를 하나 만들어주는 함수.

  • 가장 기본적 상태관리 Hook.
  • useState(데이터) ⇒ 이렇게 하면 state에 데이터를 저장할 수 있음.
  • useState 사용시

useState 사용예시

import React, {useState} from 'react';

function Example() {
	const [like, setLike] = useState(기본값); //destructuring
}
return (
	
export default Example;
  • like는 state, setLike는 like라는 state를 변경해주는 함수.

  • setLike(parameter) ⇒ parameter로 state가 변경됨

  • state가 변경되면 리렌더링된다! → 화면에 변경된 값이 표시된다.

  • destructuring 문법? 왜 저렇게 써?

    useState함수를 쓰면 다음과 같이 길이가 2인 배열이 만들어진다.

    • [상태값, 상태값을 변경해주는 함수]

      각각에 변수명을 붙여주고 싶다면

      const state = useState(0);
      const like = state[0];
      const setLike = state[1];

      이렇게 일일이 지정해줘야하는데, destructuring문법을 쓰면 코드 한줄로 가능해

      const [a, b] = ['안녕', '잘가'];  //이렇게 한번에 변수에 선언, 할당가능
      console.log(a); // '안녕'
💡 컴포넌트에서 관리해야할 상태가 여러 개면 useState 여러 번 사용하면 된다.

주의할 점

최상위(at the top level)에서만 Hook을 호출해야 합니다. 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하지 마세요.

  • React 함수 컴포넌트 내에서만 Hook을 호출해야 합니다. 일반 JavaScript 함수에서는 Hook을 호출해서는 안 됩니다. (Hook을 호출할 수 있는 곳이 딱 한 군데 더 있는데, 바로 직접 작성한 custom Hook 안. )

  • useState실습 : 한 줄 소개 입력 + 열었다 닫았다 하기

    저번에 배운 이벤트핸들링과 함께~

    import React, { useState } from "react";
    
    //CreateBio라는 함수형 컴포넌트
    function CreateBio() {
    	const [open, setOpen] = useState(false);//초기값 : false:닫혀있는 걸로 설정하기위해
    	const [text, setText] = useState("");//초기값 : 빈 문자열
    	const [bio, setBio] = useState("아직 자기소개가 없습니다.");//초기값
    	//open값을 변경해주는 함수를 실행시키는 이벤트 핸들러만들기
    	const onToggle = () => {
    		setOpen(!open); // open값을 바꿔줌(false면 true로, true면 false로)
    	}
    	const onChange = (e) => {
    		setText(e.target.value); //input창의 value = 입력값으로 text를 바꿔줌
    	}
    	const onSubmit = () => {
    		setBio(text); //text를 bio로 설정해줌.
        setText("") //설정해주고 input창은 빈칸으로
    	}
    	
    	return (
    		<>
    		{***open &&*** ( //open이 true면 {}안에있는 태그들이 return되고, false면 X
    		<input onChange={onChange} value={text}/>	//input에 입력되는 값을 text라는 state.
    		<button onClick={onSubmit}>리스트에 올리기</button>
    		)}
    		<button onClick={onToggle}>입력창 열기</button> //onClick에 onToggle함수 실행하도록
    		<h2>{text}</h2>
    		</>
    	)
    }
    
    export default CreateBio;

    input의 value에 text라는 state를 지정해줬으므로 input에 입력할 때마다 value인 text가 변경되므로 리렌더링 될 것임

    코드는여기에..

    midsummernights/react-study-prac/createbio-hook-prac at main · mogamogua/midsummernights

II. Hook 사용 이유 ?

우선 클래스형 컴포넌트, 함수형 컴포넌트를 다시 비교해보자면.

클래스형 컴포넌트

state를 가지고 있어 상태 변화에 대한 관리를 할 수 있음

단계별로 lifeCycle api를 사용할 수 있다.

life cycle?

리액트에서 화면은 언제 변화가 생기는가? →

  1. component가 mount되었을 때 (componentDidMount) - 처음 화면에 보여질때
  2. state가 변경되었을 때. (componentDidUpdate)
  3. component가 unmount될 때 (componentWillUnmount) - 화면에서 사라질때

함수형 컴포넌트

선언하기가 비교적 간편
메모리 자원을 덜 사용
state, lifecycle API사용 불가능 ⇒ hook을 도입하면서 가능해짐

HOOK을 사용하면 ?!

클래스형 컴포넌트의 문제 해결

  • 복잡한 class컴포넌트를 사용하지 않고도 lifeCycleMethod 사용가능 - 더 간단하다. shouldComponentUpdate → 의존성배열을 넣어줘서 기능 구현가능 componentDidMount, componentDidUpdate, componentWillUnmount ⇒ useEffect로 합쳐짐
  • this와 관련된 혼동문제 해결 클래스 컴포넌트 내부에서 사용하는 대부분의 함수들은 this 를 바인딩해주어야 했는데 이는 실수를 유발하고 코드가 장황해지기 쉬웠다. (사실 arrow function 사용하면 바인딩안해줘도됨)
  • 중복된 로직 반복문제 해결. 라이프사이클 api는 종종 같은 로직을 각 사이클마다 넣어줘야할 때가 있었음. hook은 상태 관련 로직을 추상화할 수 있어서 재사용성이 가능하다.
  • 코드 간결성과 가독성 : 로직을 한 곳으로 모을 수 있어서 가독성 좋음 각각의 기능들이 모여있어서 코드를 읽을 때 더 편하다. class형 컴포넌트는 서로 다른 로직이 하나의 메소드에 섞여 있어서 가독성이 좋지 않다.
    "보통 코드를 짜는 시간보다 읽기 위해 이동하는 시간이 더 오래걸린다"는 말을 생각해보면 hook을 사용하면 효율성을 높일 수 있다.
  • state 관리 가능

III. useEffect : lifeCycle 한 방에 뚝딱

특정 컴포넌트 마운트 / 언마운트 되었을 때 함수 실행되게 할 수 있음

사라질때, 값이 바뀌기전에도 함수 실행되게 할 수있음 (이건 useEffect 내에 있는 클린업 함수에서)

왜 lifecycle api가 필요하냐? → 값(상태)가 변할 때, 화면에 뭔가 변화가 생겼을 때 어떤 동작을 수행하게 하고 싶을 때가 있을 것이다.
ex) 자기소개가 업로드 되었을 때, 프로필 완성 축하 메시지를 띄워주기

useEffect 구조. 어떻게 생겼나?

  • 함수.
  • 첫번째 인자는 함수, 두번째 인자는 배열(주로 deps 라고 칭한다)이 들어간다.

useEffect(callback, dependency array)

callback함수

  • 각 생명주기마다 실행하고 싶은 함수 넣으면 됨

cleanup 함수

  • useEffect 안에서 return 할 때 실행된다.
  • useEffcet의 뒷정리를 한다. -> state에서 값 지울때 실행됨: componentWillUnmount 생명주기
  • state 값이 바뀌기 직전에 함수 실행가능 : componentShouldUpdate 생명주기

deps

  • 빈 배열을 넣거나 참조하는 state, 값을 넣어줘서 함수 실행되는 조건을 설정가능 :
  • deps 배열을 아예 생략하면, useEffect가 최신 값을 가리키지 않게 된다.

✨ deps 배열을 활용한 lifecycle에 따라 기능구현하기

마운트 될 때만 실행하고 싶을 때 : 빈 배열 넣어주기 []

컴포넌트가 화면에 맨 처음 렌더링 될 때만 설정한 함수를 실행하고, 업데이트 시엔 실행되지 않게 하려면 : 함수 두번째 파라미터로 비어있는 배열 []를 넣어주면된다!

특정 값이 업데이트 될 때+처음 렌더링될 때 실행하고 싶을 때: [변수명]

함수 두 번째 파라미터로 들어가는 배열에 검사하고 싶은 값을 넣어주면 된다.

예를 들어 name값이 변경되었을 때 함수가 실행되길 원하면

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

이렇게.

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

deps배열 아예 생략하면? (빈 배열도 넣지 않는 경우)

  • 화면 리렌더링 될 때마다 매순간 모든 컴포넌트에서 함수실행..→ 최적화에 안좋음.
  • 어떤 값을 참조하고 있을 때, deps에 해당 값을 넣어주어야만 그 값이 최신상태를 유지할 수 있음

cleanup함수

구조

useEffect(() => {
	console.log("componentDidMount 화면에 처음 나타날 때 실행되는 함수야")
	**return () => {
		console.log("componentWillUnmount 컴포넌트가 사라질 때 실행되는 함수야")	
	}**
}, [])
  1. deps배열 생략경우([]) : 컴포넌트가 사라질 때 호출됨
  2. deps배열 값이 있는 경우 : 컴포넌트 사라질 때, deps에 들어간 값이 바뀌기 직전에도 호출됨.

useEffect(func, [특정값])

여기 정리가 아주 잘 되어 있어요. useEffect 더 잘 이해하고 싶다면 ㄱ ㄱ

[번역] useEffect 완벽 가이드

  • 인풋 창 열고 닫을 때 알림 뜨게 하기 컴포넌트 안에 컴포넌트 만들었음
    import React, { useState, useEffect } from "react";
    import style from "./bio.css";
    
    //createTodo라는 함수형 컴포넌트
    function CreateBio() {
    	const [open, setOpen] = useState(false);//초기값 : false:닫혀있는 걸로 설정하기위해
    	const [text, setText] = useState("");//초기값 : 빈 문자열
    	const [bio, setBio] = useState("아직 자기소개가 없습니다.");//초기값
      **const [alert, setAlert] = useState("입력창을 열어보세요");**
    	//open값을 변경해주는 함수를 실행시키는 이벤트 핸들러만들기
    	const onToggle = () => {
    		setOpen(!open); // open값을 바꿔줌(false면 true로, true면 false로)
    	}
    
    	const onChange = (e) => {
    		setText(e.target.value); //input창의 value = 입력값으로 text를 바꿔줌
    	}
    
    	const onSubmit = () => {
    		setBio(text); //text를 bio로 설정해줌.
        setText("") //설정해주고 input창은 빈칸으로
    	}
    
      
    function InputWrapper () {
      
      **useEffect (() => {
        setAlert("입력창이 열렸어요");
        return () => {
          setAlert("입력창이 닫혔어요");
        }
      }, []);**
    
      return (
        <div className="inputWrapper">
          <input onChange={onChange} value={text} />
          <button onClick={onSubmit}> 리스트에 올리기</button>
        </div>
      )
    };
    
    	return (
    		<div id="createBio">
          {open &&  //open이 true면 {}안에있는 태그들이 return되고, false면 X
          **<InputWrapper onChange={onChange} onSubmit={onSubmit} text={text} setAlert={setAlert}/>
          }**
          <button class="toggleBtn" onClick={onToggle}>입력창 열기</button> <span>{alert}</span>
          <h2>{bio}</h2>
    		</div>
    	)
    
    };
    
    export default CreateBio;
    열고 닫을 때 알림 뜬다!! 닫을 때(닫기직전): clean up함수 실행됨. 열 때: mount되면서 callback함수 실행됨 의존배열은 빈배열로 넣어야해!! 그래야 unmount되기 직전 clean up함수실행됨 :sparkle: useEffect 예제코드 · mogamogua/midsummernights@c048649

IV. useRef

  • react 사용하면서 DOM에 접근해야 할 때.(엘리먼트의 크기를 가져오기, 스크롤바 위치 설정, 포커스 설정 등...)
  • 외부 라이브러리쓸 때 DOM에 접근해야할 때가 많아서 유용.
  • 클래스 컴포넌트에선 ref를 사용하는데 함수형 컴포넌트에선 useRef씀.
  • 원하는 위치의 DOM요소에 ref={어쩌고} 넣어주기
  • 어쩌고.current 하면 우리가 선택한 DOM의 API호출 가능해짐!!

예시

**const inputRef = useRef(null);
//useRef() 를 사용하여 Ref 객체를 만들기**
const onInsert = useCallback(() => {
	...
	**inputRef.current.focus();**
}, [])

return (
	<input **ref={inputRef}** />
)

useRef의 또다른 쓰임

  • useRef로 관리되는 변수는 값이 바뀌어도 컴포넌트가 리렌더링 되지 않음.
    → useState쓰면 값 바뀔때마다 리렌더링되는데 그렇게 하고싶지않을때 쓰자
  • 렌더링과 상관없이 바뀔 수 있는 값(로컬변수)를 사용해야 할 때도 쓰임
const nextId = useRef(4);

nextId.current += 1 // 값 바뀌어도 리렌더링 되지않음~

useRef() 를 사용 할 때 파라미터를 넣어주면, 이 값이 .current 값의 기본값이 된다.

그리고 이 값을 수정 할때에는 .current 값을 수정하면 되고 조회 할 때에는 .current 를 조회하면 된당.

  • 실습코드 : input창에 focus위치시키기 ( 일부코드 생략함) 위에서 만든 자기소개 만들기는 리스트올리기 버튼 누르면 focus가 사라짐. 이걸 계속 유지시켜보장
    import React, { useState, useRef } from "react"; //createTodo라는 함수형 컴포넌트
    function CreateBio() {
    const [open, setOpen] = useState(false);//초기값 : false:닫혀있는 걸로 설정하기위해
    const [text, setText] = useState("");//초기값 : 빈 문자열
    const [bio, setBio] = useState("아직 자기소개가 없습니다.");//초기값
    **const textInput = useRef(); // useRef함수 사용해서 Ref객체를 만들어준다. 
    // 이후, 선택하고 싶은 DOM에 속성으로 ref값을 넣어준다.**
    ... const onSubmit = () => {
    setBio(text); //text를 bio로 설정해줌.
    setText("") //설정해주고 input창은 빈칸으로 reset시키기
    textInput.current.focus(); // .current로 DOM api 접근가능. => focus되게
    }
    return (
    {open &&
    ... ) }; export default CreateBio; ```

:sparkle: useRef사용하여 focus 설정하기 · mogamogua/midsummernights@aca2a6a

시간관계상 useRef로 dom api다루는 것만 하지만 다른 용도로도 사용하는 코드 작성해보면좋을듯!

수정

profile
디자인과 기획이 재미있는 프론트엔드 개발자입니다. 블로그 이사 준비중. . .

1개의 댓글

comment-user-thumbnail
알 수 없음
2021년 12월 15일
수정삭제

삭제된 댓글입니다.

1개의 답글