ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 초보자들이 자주 실수하는 리액트 훅 Top 6
    Dev/React.js 2023. 3. 8. 01:49
    반응형
    Youtube를 보다가 'Top 6 React Hook Mistakes Beginners Make' 라는 관심을 가질 만한 영상을 찾았다. 이 영상을 보고 나는 얼마나 실수를 하고 있으며, 실수를 하고 있다면 고치기 위하여 해당 내용을 정리하면서 포스팅하기로 하였다. 리액트 훅은 기본적으로 알고 있다고 생각하고 진행하겠다.

     

    1. Using state when you don't need it

    import React, { useState } from "react";
    
    function Mistakes() {
      const [email, setEmail] = useState("");
      const [password, setPassword] = useState("");
    
      function onSubmit(e) {
        e.preventDefault();
        console.log({ email, password });
      }
    
      return (
        <form onSubmit={onSubmit}>
          <label htmlFor="email">Email</label>
          <input
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            type="email"
            id="email"
          ></input>
          <label htmlFor="password">Password</label>
          <input
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            type="password"
            id="password"
          ></input>
          <button type="submit">Submit</button>
        </form>
      );
    }
    
    export default Mistakes;

     

    위의 예제 코드가 있다. 어떤 문제가 있을까? 실제로 해당 코드는 submit을 할 때, 필요한 값을 사용한다.

    하지만 리액트에서는 input 태그에서 값을 입력할 때 마다 상태를 추적하고 다시 랜더링을 한다. 

     

    import React, { useRef } from "react";
    
    function Mistakes() {
      const emailRef = useRef(); 
      const passwordRef = useRef();
    
      function onSubmit(e) {
        e.preventDefault();
        console.log({
          email: emailRef.current.value,
          password: passwordRef.current.value,
        });
      }
    
      return (
        <form onSubmit={onSubmit}>
          <label htmlFor="email">Email</label>
          <input ref={emailRef} type="email" id="email"></input> 
          <label htmlFor="password">Password</label>
          <input ref={passwordRef} type="password" id="password"></input>
          <button type="submit">Submit</button>
        </form>
      );
    }
    
    export default Mistakes;

     

    위와 같이 useState를 사용하지말고, useRef를 사용하여 불필요한 리랜더링을 막자.

     

    2. Not using the function version of useState

    import React, { useState } from "react";
    
    function Mistakes() {
      const [count, setCount] = useState(0);
    
      function adjustCount(amount) {
        setCount(count + amount);
      }
      return (
        <>
          <button onClick={() => adjustCount(-1)}>-</button>
          <span>{count}</span>
          <button onClick={() => adjustCount(1)}>+</button>
        </>
      );
    }
    
    export default Mistakes;

     

    아무 문제가 없어보이지만 실제로는 이전 카운트 변수를 사용하여 계정 변수를 업데이트하고 있다. 이러한 경우에는 항상 useState Setter의 함수 버전을 사용해야 한다.

     

    현재는 간단한 예제라서 별다른 문제가 없어보이지만, 해당 변수가 서로 연관있는 변수가 있을 때 예기치 못한 상황을 발생시킬 수 있다. 

     

    import React, { useState } from "react";
    
    function Mistakes() {
      const [count, setCount] = useState(0);
    
      function adjustCount(amount) {
        setCount((currentCount) => currentCount + amount);
      }
      return (
        <>
          <button onClick={() => adjustCount(-1)}>-</button>
          <span>{count}</span>
          <button onClick={() => adjustCount(1)}>+</button>
        </>
      );
    }
    
    export default Mistakes;

     

    항상 업데이트 시에는 setter의 함수버전을 사용하여 최신화 된 값을 보장하도록 하자. 

     

    아래는 setter함수를 사용하지 않았을 때와 사용했을 때의 값 비교이다.  사용하지 않는다면 최신화 된 값을 보장하지 못한다. 

     

    // 잘못 사용된 예
    function adjustCount(amount) {
     setCount(count + amount // 0 + 1
     setCount(count + amount) // 0 + 1
    }
    
    // 올바른 예
    function adjustCount(amount) {
      setCount((currentCount) => currentCount + amount); // 0 + 1
      setCount((currentCount) => currentCount + amount); // 1 + 1
    }

    3. state does not update immediately

    state를 업데이트 하여도 다음 렌더링 까지는 변경되지 않는다.

     

    위의 코드에서 adjustCount 함수안에서 count를 찍어보자. 그리고 + 버튼을 누르면 count의 값은 올라가지만, 콘솔에는 숫자가 0일 것이다. 이러한 경우에 useEffect를 사용하여 count를 종속성 배열에 추가해주면, 원하는대로 동작할 것이다.

     

    4. unnecessaty useEffects

    import React, { useState, useEffect } from "react";
    
    function Mistakes() {
      const [firstName, setFirstName] = useState("");
      const [lastName, setLastName] = useState("");
      const [fullName, setFullName] = useState("");
    
      useEffect(() => {
        setFullName(`${firstName} ${lastName}`);
      }, [firstName, lastName]);
    
      return (
        <>
          <input value={firstName} onChange={(e) => setFirstName(e.target.value)}></input>
          <input value={lastName} onChange={(e) => setLastName(e.target.value)}></input>
          {fullName}
        </>
      );
    }
    
    export default Mistakes;

     

    위의 코드 같은 경우에는 입력때마다 리랜더링이 된다. 그 후 useEffect때문에 실제로 두 번 렌더링하게 된다. 중복이 되기 때문에 fullName은 같은 경우는 변수로 그대로 사용하여야 한다.

    const fullName = firstName + lastName;

     

    다른 값을 업데이트하기 위해 값이 변경될 때 항상 useEffect를 사용해야 하는 것은 아님을 알아야 한다.

     

     

    5. Referential equality mistakes

    import React, { useState, useEffect } from "react";
    
    function Mistakes() {
      const [age, setAge] = useState(0);
      const [name, setName] = useState("");
      const [darkMode, setDarkMode] = useState(false);
    
      const person = { age, name };
    
      useEffect(() => {
        console.log(person);
      }, [person]);
      
      return (
        <div style={{ background: darkMode ? "#333" : "#FFF" }}>
          Age :
          <input
            value={age}
            type="number"
            onChange={(e) => setAge(e.target.value)}
          ></input>
          <br></br>
          Name :
          <input value={name} onChange={(e) => setName(e.target.value)}></input>
          DarkMode :
          <input
            type="checkbox"
            value={darkMode}
            onChange={(e) => setDarkMode(e.target.checked)}
          ></input>
        </div>
      );
    }
    
    export default Mistakes;

    위의 코드는  person객체를 종속성 배열에 추가하였다. person객체에서 age와 name만을 가지고 있다. 그래서 darkMode에 대한 변경사항은 상관없을 것만 같다. 하지만 실제로 darkMode의 값이 달라지면 person이 계속 콘솔에 찍힌다. 왜 그런 것일까?

     

    이것은 javascript의 문법이다. 동일한 속성을 가질 수 있지만 완전히 새로운 개체이다. 동일한 값을 가질 수는 있어도 기술적으로 서로 다르다. person은 객체이고, 참조형이기 때문이다. 

     

    그렇다면, 이러한 경우에는 어떻게 해결해야 하는가? 이때, 사용하는 것이 useMemo이다.

     

     const person = useMemo(() => {
        return { age, name };
      }, [age, name]);

     

    6. Not aborting fetch requests

    fetch에 대한 부분을 custom hook으로 만들었을 때, clean-up을 해주어야 한다는 내용이다. 

     

    clean-up시 사용자 작업에서 fetch가 더 이상 필요하지 않다고 알려야하는 경우에는 아래의 사이트를 통해 좀더 자세히 살펴보면 좋을 것 같다. 

     

     

     


     

    잘못된 부분이나 보완할 점이 있으면 댓글 달아주시면 감사하겠습니다. 😉

     

     

     

     

     

     


     

    Top 6 React Hook Mistakes Beginners Make : https://youtu.be/GGo3MVBFr1A

    Fetch: Abort : https://ko.javascript.info/fetch-abort

    Effect Hook ( Clean-up ) : https://velog.io/@enjoywater/React-Effect-Hook-Clean-up

    반응형

    'Dev > React.js' 카테고리의 다른 글

    React Query 소개 및 사용하기  (7) 2023.03.09
    useState와 useReducer 둘 중 무엇을 사용해야 할까?  (14) 2023.03.05

    댓글

Designed by Tistory.