본문 바로가기
React

리액트 팁 및 클린코드 - 3. Props

by 맨날개발 2024. 10. 26.
Props와 관련 된 클린코드 

상태를 끌어올리는 것을 주의하라

결론 : 의미없는 상태 끌어올림은 의미없는 리렌더링을 발생시킨다.

 

자식 요소만 리렌더링 되어야 하는 경우에도 상태가 끌어올려져서 부모 컴포넌트가 리렌더링 되는 문제가 발생한다.

부모에서 다른 자식 컴포넌트를 사용하는 경우 다른 자식 컴포넌트도 리렌더링 되어 성능에 좋지 못하다.

 

더보기

예를 들어 input과 관련 된 상태를 부모로 끌어올리는 경우 사용자가 타이핑 할때마다 해당 상태를 관리하는 컴포넌트는 매번 리렌더링 된다. 그래서 input은 관련된 컴포넌트에 두고 최종 결과만 부모 컴포넌트에 전달해서 사용하는 방법을 사용하는 것이 성능상 좋다.

 

잘못 된 예

아래는 input을 변경할 때마다 부모의 상태를 변경한다. 이로인해 부모가 리렌더링 되면서 Form 컴포넌트 이외의 다른 컴포넌트까지 리렌더링 된다.

import { useState } from 'react';

const Parent = () => {
  const [form, setForm] = useState({ id: '', email: '' });

  const handleChange = (e) => {
    setForm((form) => ({
      ...form,
      [e.target.name]: e.target.value
    }));
  };

  return (
	  <>
	    <Form onChange={handleChange} form={form} />
	    <List />
    </>
  )
};

const Form = ({ onChange, form }) => {
  return (
    <form>
      <input name="id" value={form.id} onChange={onChange} />
      <input name="email" value={form.email}  onChange={onChange} />
    </form>
  )
};

 

수정 후 코드

상태를 Form으로 옮겼기 때문에 이제 input을 수정해도 다른 컴포넌트를 리렌더링 되지 않는다.

import { useState } from 'react';

const Parent = () => {
	const handleSubmit = () {
	
	}

  return (
    <>
      <Form onSubmit={handleSubmit} />
      <List />
    </>
  )
};

const Form = ({ onSubmit }) => {
  const [form, setForm] = useState({ id: '', email: '' });

  const handleChange = (e) => {
    setForm((form) => ({
      ...form,
      [e.target.name]: e.target.value
    }));
  };

  return (
    <form onSubmit={onSubmit}>
      <input name="id" value={form.id} onChange={handleChange} />
      <input name="email" value={form.email}  onChange={handleChange} />
      <button type="submit">Submit</button>
    </form>
  )
};

 

객체를 전달하지 마라

결론 : 자식요소에 객체 형태로 전달하는 경우 원치 않는 리렌더링을 발생시킬 수 있다.

 

보통 부모가 리렌더링 되면 자식도 리렌더링 된다. 그래서 부모에게서 전달받은 상태의 값이 변경되지 않는 경우 리렌더링 되지 않도록 하기 위해 React.memo 를 사용하게 된다.

 

이때 값이 변경되지 않는 것을 판단하는 것은 단순 === 비교이다. 객체를 전달하는 경우 리렌더링 되면서 매번 새로운 객체가 생성되기 때문에 객체 === 객체 는 항상 false가 나오게 된다.

 

이로인해서 객체를 전달하면 값이 변경되지 않아도 리렌더링이 발생한다.

💡 물론, React.memo 에서 직접 비교하는 함수를 전달해도 되긴 하지만 객체 전달은 자제하자.

 

더보기

잘못된 예제

리렌더링을 최소화하고자 React.memo 를 사용했지만 객체를 전달하기 때문에 사실상 의미없는 코드가 되었다.

const Form = ({ form }) => {
  return (
    <form>
      <input value={form.id} />
      <input value={form.email} />
    </form>
  )
};

const MemoizedForm = React.memo(Form);

function App() {
  const [form, setForm] = useState({ id: '', email: '' });

  return (
    <>
      <MemoizedForm form={form} />
    </>
  )
}

 

수정 후 코드

객체가 아닌 원시값을 전달한다.

const Form = ({ id, email }) => {
  return (
    <form>
      <input value={id} />
      <input value={email} />
    </form>
  )
};

const MemoizedForm = React.memo(Form);

function App() {
  const [form, setForm] = useState({ id: '', email: '' });

  return (
    <>
      <MemoizedForm {...form} />
    </>
  )
}

 

자식요소에게 Props 전달 시 스프레드 연산자 주의

결론 : 스프레드 연산을 사용하는 경우 코드 예측이 어렵고 불필요한 값까지 전달할 가능성이 높다.

 

자식요소에게 상태를 전달할 때 스프레드 연산자를 활용하면 코드도 간결하고 간편하게 전달할 수 있다. 전달할 값이 많을 수록 코드 작성이 용이해진다.

 

const form = {
	id: '',
	email: ''
};

return (
	<Child {...form}>
);

 

하지만 스프레드 연산자를 사용하는 경우 어떠한 값을 전달하는지 한눈에 확인이 어렵다. 추가적으로 객체를 한번에 전달하기 때문에 객체에 불필요한 값이 포함되어 전달될 가능성이 존재한다.

 

이는 불필요한 상태 값의 변경으로 인해 불필요한 리렌더링을 유발할 수 있다.

스프레드 연산자는 간편하게 상태를 전달할 수 있기 때문에 주의해서만 잘 사용하자!

💡 스프레드 연산자를 사용한다면, 자식 요소에게 필요한 값만 전달하자!

 

더보기

잘못 된 예제

Form 컴포넌트는 id만 필요하지만 form 객체를 스프레드 연산자를 통해 전달하면서 불필요한 값까지 전달하고 있다.

const Form = ({ id }) => {
  return (
    <form>
      <input value={id} />
    </form>
  )
};

function App() {
  const [form, setForm] = useState({ id: '', email: '', password: '' });

  return (
    <>
      <Form {...form} />
    </>
  )
}

 

수정 후 코드

사실상 Form 컴포넌트가 하나만 받기 때문에 스프레드를 사용하는 것이 이상하지만, 예제로써 이해해보자.

아래와 같이 사용되지 않는 값은 제외하고 실제로 사용하는 값만 스프레드 연산자를 통해서 전달하자!

 

const Form = ({ id }) => {
  return (
    <form>
      <input value={id} />
    </form>
  )
};

function App() {
  const [form, setForm] = useState({ id: '', email: '', password: '' });
	const { id, email, ...props } = form;

  return (
    <>
      <Form {...props} />
    </>
  )
}

 

너무 많은 Props 전달

결론 : 컴포넌트가 너무 많은 일을 한다면 컴포넌트를 분리하자.

 

자식요소가 너무 많은 Props를 전달할 것을 요구한다면 해당 컴포넌트가 너무 많은 기능을 하고 있지 않은가 고민해봐야 한다.