development

초보자를 위한 리액트(React) 개발 팁

여름싼타 2022. 11. 30. 09:53
반응형

리액트 초보자를 위한 7가지 팁 정리

 

1. 컴포넌트 name 설정

버그가 있는 컴포넌트를 파악하려면 항상 컴포넌트에 이름을 지정하는 것이 중요하다. React Router 또는 외부 라이브러리를 사용하기 시작하면 더욱 그렇다.

// Avoid thoses notations 
export default () => {};
export default class extends React.Component {};

default본 export를 사용할지 named export를 사용할지에 대한 논쟁이 있다. default export 컴포넌트명이 프로젝트에서 일관되도록 보장하지 않는다. 게다가 트리 쉐이킹에 덜 효과적이다.

 

2. 컴포넌트를 expose하는 방법에 관계없이 이름 설정

컴포넌트를 호스팅 하는 클래스 이름 또는 변수명을 정의해야 한다. 리액트는 실제로 오류 메시지에서 컴포넌트명을 유추한다.

export const Component = () => <h1>I'm a component</h1>;
export default Component;

// Define user custom component name
Component.displayName = 'My Component';

그리고 ESLint를 사용하는 경우 다음 두 가지 규칙을 설정하는 것을 고려해야 한다.

"rules": {
    // Check named import exists
    "import/named": 2, 
  
    // Set to "on" in airbnb preset
    "import/prefer-default-export": "off"
}

 

3. Functional 컴포넌트 사용

데이터 표시만을 목적으로 하는 컴포넌트가 많은 경우 React 컴포넌트를 정의하는 다양한 방법을 활용한다.

class Watch extends React.Component {
  render () {
    return <div>{this.props.hours}:{this.props.minutes}</div>
  }
}

// Equivalent functional component
const Watch = (props) =>
  <div>{props.hours}:{props.minutes}</div>;

방법 모두 동일한 Watch 컴포넌트를 정의한다. 그러나 두 번째는 JSX 템플릿의 props에 접근하기 더 짧고 this가 필요 없다.

 

4. div를 프래그먼트(Fragments)로 바꾸기

모든 컴포넌트는 고유한 루트 요소를 템플릿으로 노출해야 한다. 이 규칙을 준수하기 위한 일반적인 수정은 템플릿을 div로 감싸는 것이었다. 하지만 리액트 16부터 Fragments라는 새로운 기능을 제공한다. 쓸모없는 divs를 React.Fragments로 바꿀 수 있다. 출력 템플릿은 래퍼가 없는 Fragment 콘텐츠다.

const Login = () => 
  <div><input name="login"/><input name="password"/></div>;

const Login = () =>
  <React.Fragment><input name="login"/><input name="password"/></React.Fragment>;

const Login = () => // Short-hand syntax
  <><input name="login"/><input name="password"/></>;

 

5. 상태를 설정할 때 주의하기

리액트 앱이 동적이 되는 즉시 컴포넌트의 상태를 처리해야 한다. constructor에서 상태를 초기화한 다음 setState로 상태를 설정한다. 어떤 이유로 상태의 값을 설정하기 위해 setState를 호출할 때 현재 상태 또는 소품 값을 사용해야 할 수도 있다.

// Very bad pratice: do not use this.state and this.props in setState !
this.setState({ answered: !this.state.answered, answer });

// With quite big states: the tempatation becomes bigger 
// Here keep the current state and add answer property
this.setState({ ...this.state, answer });

문제는 리액트가 this.state와 this.props의 값을 보장하지 않고 가지고 있다는 것이다. 상태 업데이트가 DOM 조작을 최적화하기 위한 일괄 처리이기 때문에 this.state는 비동기로 동작한다.

// Note the () notation around the object which makes the JS engine
// evaluate as an expression and not as the arrow function block
this.setState((prevState, props) => ({ ...prevState, answer}));

setState를 사용할 때 상태 손상을 예방하려면 매개변수와 함께 사용해야 한다.

 

6. Binding 컴포넌트 functions

element의 이벤트를 컴포넌트에 바인딩하는 방법에는 여러 가지가 있지만 일부는 권장되지 않는다. 첫 번째 방법은 리액트 문서에 나와있다.

class DatePicker extends React.Component {
   handleDateSelected({target}){
     // Do stuff
   }
   render() {   
     return <input type="date" onChange={this.handleDateSelected}/>
   }
 }

위 코드는 작동하지 않는다. 그 이유는 JSX를 사용할 때 this 값이 컴포넌트 인스턴스에 바인딩되지 않기 때문이다. 작동하도록 하는 방법은 다음과 같다.

// #1: use an arrow function
<input type="date" onChange={(event) => this.handleDateSelected(event)}/>

// OR #2: bind this to the function in component constructor
constructor () { 
  this.handleDateSelected = this.handleDateSelected.bind(this); 
}

// OR #3: declare the function as a class field (arrow function syntax)
handleDateSelected = ({target}) => {
   // Do stuff
}

첫 번째 예에서와 같이 JSX에서 화살표 함수를 사용하는 것이 처음에는 매력적으로 보인다. 하지만 이렇게 하면 안 된다. 실제로 각 컴포넌트 렌더링 시 화살표 기능이 다시 생성되어 성능이 저하된다.

 

7. 컨테이너 패턴 사용 (Redux 포함)

마지막으로 컨테이너 디자인 패턴이다. 이를 통해 리액트 컴포넌트의 관심사 분리 원칙을 따를 수 있다.

export class DatePicker extends React.Component {
  state = { currentDate: null };

  handleDateSelected = ({target}) =>
     this.setState({ currentDate: target.value });

  render = () => 
     <input type="date" onChange={this.handleDateSelected}/>
}

단일 컴포넌트가 동일한 위치에서 템플릿 렌더링 및 사용자 작업을 처리한다. 대신 두 가지 컴포넌트를 사용하자.

const DatePicker = (props) => 
  <input type="date" onChange={props.handleDateSelected}/>
        
export class DatePickerController extends React.Component { 
  // ... No changes except render function ...
  render = () => 
     <DatePicker handleDateSelected={this.handleDateSelected}/>;
}

여기에 트릭이 있다. DatePickerContainer가 필요한 경우 사용자 상호 작용 및 API 호출을 처리한다. 그런 다음 DatePicker를 렌더링하고 supplies props를 전달한다. 이 패턴 덕분에 컨테이너 컴포넌트가 프레젠테이션 컴포넌트를 대체할 수 있다. functional component는 props가 없으면 쓸모가 없다.

export const DatePickerContainer = 
 connect(mapStateToProps, mapDispatchToProps)(DatePickerController);

또한 Redux를 앱의 상태 관리자로 사용하는 경우에도 이 패턴과 잘 연결된다. 이 connect함수는 컴포넌트에 props를 주입한다. 우리의 경우 해당 props를 컴포넌트로 전달할 컨트롤러를 공급합니다. 따라서 두 컴포넌트 모두 Redux 데이터에 접근 할 수 있다.

 

참고

https://medium.com/free-code-camp/the-beginners-collection-of-powerful-tips-and-tricks-for-react-f2e3833c6f12

반응형