리액트 Best Practice & Pattern 2탄

Sheryl Yun·2023년 8월 6일
1

리액트 Best Practice

목록 보기
3/4
post-thumbnail

** 리액트 코드 모범 사례 (airbnb 기반)과 중복되는 내용은 생략

동시에 업데이트되는 useState: 하나의 객체로 선언

  • state 변수를 여러 개 선언하면 불필요한 리렌더링이 발생한다.
  • React 18 버전부터 automatic batching을 적용하여 여러 번의 setState를 함수 실행이 끝난 후 한 번에 처리한다.
    (리렌더링이 한 번만 발생)

Bad

const [name, setName] = useState('');
const [count, setCount] = useState(0);

const onClick = () => {
    setName('John');
    setCount(count + 1);
};

Good

  • 기존 값을 변경 없이 그대로 넣어줄 때 spread 문법 사용
const [state, setState] = useState({
    name: '',
	count: 0,
});

const onClick =() => {
    setState((prev) => ({
      	...prev,
        name: 'John',
        count: prev.count + 1
    }));
};

styled-components: theme 코드 모듈화

  • jsx에 인라인 스타일로 선언하기 지양
  • styled-components를 활용하여 따로 스타일 컴포넌트를 만들어 선언하기
  • theme 부분은 여러 군데서 반복될 여지가 많으므로 따로 변수화(모듈화)하여 재사용한다.

Bad

return <div style={{ backgroundColor: "red" }}></div>;

Good

const Container = styled.div`
	background-color: ${({ theme }) => theme.colors.background};
`;

Better

const getPrimaryColor = ({ theme }) => theme.colors.primary;
const getDefaultColor = ({ theme }) => theme.colors.secondary;

const Button = styled.button`
	background-color: ${getPrimaryColor};
    color: ${getDefaultColor};
`;

클래스형 컴포넌트 대신 함수형 사용하기

  • 클래스형을 함수형으로 바꾸면 좋은 점
    • 개발 시 코드 작성 시간이 짧아진다.
    • production 번들 크기를 거의 60%까지 줄일 수 있다.

Bad (클래스형)

class Counter extends Component {
	constructor(props) {
    	super(props);
        this.state = { count: 0 };
        this.onClick = this.onClick.bind(this);
    }
    
    onClick = () => {
    	this.setState({
        	count: this.state.count + 1,
        });
    };
    
    render() {
    	return <button onClick={onClick}>Click me</button>;
    }
}

Good (함수형)

const Counter = () => {
	const [count, setCount] = useState(0);
    
    const onClick = () => setCount(prev => prev + 1);
    
    return <button onClick={onClick}>Click me</button>;
};

React.memo로 불필요한 리렌더링 막기

  • 컴포넌트를 메모이제이션(memoization)하여 불필요한 리렌더링을 막는다.
  • 주의: props를 전달 받고 자주 리렌더링되는 컴포넌트는 React.memo가 불필요
    아래 예시의 경우 아무 props도 전달 받지 않고 매번 리렌더링될 필요 없기 때문에 React.memo가 적합

Bad

return (
	<ul>
    	{items.map((item) => (
        	<Component>{item}</Component>
        ))}
    </ul>
);

Good

const MemoComponent = React.memo(Component);

return (
	<ul>
    	{items.map((item) => (
        	<MemoComponent>{item}</MemoComponent>
        ))}
    </ul>
);

여러 개 컴포넌트 로딩 시 switch문 대신 객체 + lazy 조합 (NotFound 처리 포함)

Bad

switch(props.type) {
	case "ADMIN":
    	return <Admin />;
    case "USER":
    	return <User />;
    default: 
    	return <NotFound />;
}

Good

  • ?? 연산자: 좌항(componentMap[props.type]) 값이 없을 경우 우항(NotFound) 반환
const componentMap = {
	ADMIN: Admin,
    USER: User,
};

const Component = componentMap[props.type] ?? NotFound;

return <Component />;

Better

  • React.lazy: 필요한 시점에 컴포넌트 불러오기 (code splitting, 동적 import)
  • NotFound 변수: 반환 값이 '컴포넌트'이므로 앞 글자를 대문자로 작성
const NotFound = React.lazy(() => import('../components/NotFound'));

const componentMap = {
	ADMIN: React.lazy(() => import('../components/Admin')),
    USER: React.lazy(() => import('../components/User')),
};

const Component = componentMap[props.type] ?? NotFound;

return <Component />;

if문 안에 'return'문이 있으면 else문 생략하기

  • return은 코드 라인을 실행하고 '종료'하는 역할까지 함
    • if문 안의 return문이 실행된다면 자동으로 다음 라인의 코드는 실행되지 않으므로 다른 분기 시 else문 불필요

Bad

if (props.name) {
	return <div>{props.name}</div>;
} else {
	return <div>이름 없음</div>;
}

Good

if (props.name) {
	return <div>{props.name}</div>;
}

return <div>이름 없음</div>;

참고 자료

React best practices and patterns to reduce code - Part 2

profile
데이터 분석가 준비 중입니다 (티스토리에 기록: https://cherylog.tistory.com/)

0개의 댓글