FE/리뷰

[FE / REVIEW] 0215 Article 리뷰

따봉치치 2024. 2. 15. 10:43

 

1. 꼭 써봐야 하는 10가지 리액트 커뮤니티 훅들

 

  • react-hook-form 라이브러리의 useForm : 간단한 코드로 입력값 검증하고 에러처리 가능
  • react-use 라이브러리의 useUpdateEffect : 특정 상태가 업데이트 될 때 함수 호출 가능
import { useUpdateEffect } from "react-use"

const Home = () => {
  useUpdateEffect(() => {
    // 이 함수는 첫 렌더링에 실행되지 않습니다
    console.log("Runs only if the state updates")
  }, [fName])

  return ( ...)
}
  • react-use 라이브러리의 useCopyToClipboard : 상태(사용자가 클립보드에 복사하는 문자열 값)와, 상태를 업데이트하고 클립보드에 복사하는 함수 반환
import { useCopyToClipboard } from "react-use";

const App = () => {
  const [text, setText] = useState("");
  const [state, copyToClipboard] = useCopyToClipboard();

  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <button type="button" onClick={() => copyToClipboard(text)}>
        copy text
      </button>
    </div>
  );
};
  • react-use 라이브러리의 useLocalStotage : 로컬 스토리지 손쉽게 접근 가능
import { useLocalStorage } from "react-use";

export default function App() {
  const [token, setToken, removeToken] = useLocalStorage("token", "foo"); // 초기값은 foo 입니다

  return (
    <div>
      <div>Value: {token}</div>

      <button onClick={() => setToken("bar")}>bar</button>

      <button onClick={() => setToken("baz")}>baz</button>

      <button onClick={() => removeToken()}>Remove</button>
    </div>
  );
}
  • react-use 라이브러리의 useHover : 특정 요소가 hover 됐는지 확인할 때 사용
import { useHover } from "react-use";

const App = () => {
  // 여기에서 함수의 hovered 인자를 사용할 수 있습니다
  const element = (hovered: boolean) => (
    <p>Sample text which is {hovered ? "hovered" : "not hovered"}</p>
  );
  // 혹은 훅으로부터 hovered 상태값을 얻을 수 있습니다
  const [textElement, isHovered] = useHover(element);

  return <div>{textElement}</div>;
};
  • react-use 라이브러리의 useIdle : 사용자가 유휴 상태인지 활성 상태인지 확인하기 위해 사용
import { useIdle } from "react-use";

const App = () => {
  const isIdle = useIdle(3000); // 만약 사용자가 3초 이상 유휴상태가 되면 true를 반환합니다

  return <div>{isIdle ? "User is idle" : "User is not idle"}</div>;
};
  • react-use 라이브러리의 useClickAway : 특정 UI 컴포넌트의 외부를 클릭할 때마다 함수를 트리거할 때 사용
import { useClickAway } from "react-use";

const App = () => {
  const ref = useRef(null);

  useClickAway(ref, () => {
    console.log("OUTSIDE CLICKED");
  });

  return (
    <div
      ref={ref}
      style={{
        width: 200,
        height: 200,
        background: "red",
      }}
    />
  );
};
  • react-use 라이브러리의 useDebounce : 사용자가 입력을 마칠 때까지 기다린 후 요청 보냄
import { useDebounce } from "react-use";

const App = () => {
  const [input, setInput] = useState("");
  const [loading, setLoading] = useState(false);

  useDebounce(
    () => {
      setLoading(true);
      // 여기에서 api 요청을 합니다
      // ...
      // ...
      setLoading(false);
    },
    500,
    // 함수를 실행하기 전 기다릴 시간(밀리초)입니다
    [input] // 변화를 감지할 의존성값입니다
  );

  return <input value={input} onChange={(e) => setInput(e.target.value)} />;
};
  • react-use 라이브러리의 useWindowSize : 스크린의 너비, 높이 값 반환하고 스크린 사이즈가 변할 때 자동으로 그 값 업데이트
import { useWindowSize } from "react-use";

const App = () => {
  // window의 높이와 너비값을 가져옵니다
  const { width, height } = useWindowSize();

  return (
    <div>
      <div>width: {width}</div>

      <div>height: {height}</div>
    </div>
  );
};
  • swr 라이브러리의 useSwr : 컴포넌트가 마운트 될 때 발생하는 요청 쉽게 처리
import useSWR from "swr";

const fetcher = (url: string) => fetch(url).then((r) => r.json());

const App = () => {
  const { data, mutate, error, isValidating } = useSWR(
    "https://jsonplaceholder.typicode.com/todos/1",
    fetcher
  );

  if (error) return <div>failed to load</div>;
  if (!data) return <div>loading...</div>;
  return <div>Todo title: {data.title}!</div>;
};

 

 

2. 리액트에서 useState를 사용하면서 저지를 수 있는 흔한 실수들

 

  • 불필요한 상태 변수
function RedundantState() {
  const [firstName, setFirstName] = useState("");
  const [lastName, setLastName] = useState("");
  const [fullName, setFullName] = useState("");

  const onChangeFirstName = (event) => {
    setFirstName(event.target.value);
    setFullName(`${event.target.value} ${lastName}`);
  };

  const onChangeLastName = (event) => {
    setLastName(event.target.value);
    setFullName(`${firstName} ${event.target.value}`);
  };
  
  .
  .
  .

 

다음과 같이 수정 가능

export function RedundantState() {
  const [firstName, setFirstName] = useState("");
  const [lastName, setLastName] = useState("");

  const fullName = `${firstName} ${lastName}`;

  ...

  return (
    <>
      <form>
        ...
      </form>
      <div>Full name: {fullName}</div>
    </>
  );
}

 

  • 중복 상태
function DuplicateState({ items }) {
  const [selectedItem, setSelectedItem] = useState();

  const onClickItem = (item) => {
    setSelectedItem(item);
  };

  return (
    <>
      {selectedItem && <Modal item={selectedItem} />}
      <ul>
        {items.map((row) => (
          <li key={row.id}>
            {row.text}
            <button onClick={() => onClickItem(row)}>Open</button>
          </li>
        ))}
      </ul>
    </>
  );
}

 

다음과 같이 수정 가능

아이템 자체를 복사하는 것이 아니라 id 값만 가져옴

아이템이 중복되게 발생하면 만약 사용자가 아이템을 수정했을 때 불필요하게 로직이 복잡해질 수 있다

function DuplicateState({ items }) {
  const [selectedItemId, setSelectedItemId] = useState();
  const selectedItem = items.find(({ id }) => id === selectedItemId);

  const onClickItem = (itemId) => {
    setSelectedItemId(itemId);
  };

  return (
    <>
      {selectedItem && <Modal item={selectedItem} />}
      <ul>
        {items.map((row) => (
          <li key={row.id}>
            {row.text}
            <button onClick={() => onClickItem(row.id)}>Open</button>
          </li>
        ))}
      </ul>
    </>
  );
}

 

  • useEffect를 통한 상태 업데이트
function DuplicateState({ items }) {
  const [selectedItem, setSelectedItem] = useState();

  useEffect(() => {
    if (selectedItem) {
      setSelectedItem(items.find(({ id }) => id === selectedItem.id));
    }
  }, [items]);

  const onClickItem = (item) => {
    setSelectedItem(item);
  };

  return (
    <>
      {selectedItem && <Modal item={selectedItem} />}
      <ul>
        {items.map((row) => (
          <li key={row.id}>
            {row.text}
            <button onClick={() => onClickItem(row)}>Open</button>
          </li>
        ))}
      </ul>
    </>
  );
}

 

useEffect가 적을 수록 좋다

useEffect 내에서 상태를 업데이트하면 추가 렌더링 발생

function DuplicateState({ items }) {
  const [selectedItemId, setSelectedItemId] = useState();
  const selectedItem = items.find(({ id }) => id === selectedItemId);

  const onClickItem = (id) => {
    setSelectedItem(id);
  };

  return (
    <>
      {selectedItem && <Modal item={selectedItem} />}
      <ul>
        {items.map((row) => (
          <li key={row.id}>
            {row.text}
            <button onClick={() => onClickItem(row.id)}>Open</button>
          </li>
        ))}
      </ul>
    </>
  );
}