javascript/react at master · firejune/javascript

Airbnb React/JSX 스타일 가이드

React와 JSX에 대한 가장 합리적인 접근 방식

목차

  1. 기본 규칙(Basic Rules)
  2. 클래스 대 React.createClass(Class vs React.createClass)
  3. 명명(Naming)
  4. 선언(Declaration)
  5. 조정(Alignment)
  6. 인용(Quotes)
  7. 공백(Spacing)
  8. 속성(Props)
  9. 괄호(Parentheses)
  10. 태그(Tags)
  11. 메소드(Methods)
  12. 호출순서(Ordering)
  13. isMounted

기본 규칙(Basic Rules)

클래스 대 React.createClass(Class vs React.createClass)

  • 특별한 이유로 믹스인(mixin)하는 경우를 제외하고는 class extends React.Component를 사용하세요.

eslint rules: react/prefer-es6-class.

```javascript
// bad
const Listing = React.createClass({
  render() {
    return <div />;
  }
});

// good
class Listing extends React.Component {
  render() {
    return <div />;
  }
}
```

명명(Naming)

  • 확장자: React 컴포넌트는 .jsx 확장자를 사용합니다.
  • 파일명: 파일명에는 PascalCase(대문자로 시작)를 사용합니다. 예), ReservationCard.jsx.
  • 참조명: React 컴포넌트의 참조 이름에는 PascalCase를 쓰고 그 인스턴스의 이름에는 camelCase(소문자로 시작)를 사용합니다.

eslint rules: react/jsx-pascal-case.

```javascript
// bad
import reservationCard from './ReservationCard';

// good
import ReservationCard from './ReservationCard';

// bad
const ReservationItem = <ReservationCard />;

// good
const reservationItem = <ReservationCard />;
```
  • 컴포넌트명: 컴포넌트명으로 파일명을 씁니다. 예), ReservationCard.jsx 파일은 ReservationCard라는 참조명을 가집니다. 그러나, 루트 컴포넌트가 디렉토리에 구성되었다면 파일명을 index.jsx로 쓰고 디렉토리명을 컴포넌트명으로 사용합니다:

    // bad
    import Footer from './Footer/Footer';
    
    // bad
    import Footer from './Footer/index';
    
    // good
    import Footer from './Footer';

선언(Declaration)

  • displayName을 이용하여 컴포넌트명을 정하지 않습니다. 그대신, 참조에 의해 이름을 지정합니다.

    // bad
    export default React.createClass({
      displayName: 'ReservationCard',
      // stuff goes here
    });
    
    // good
    export default class ReservationCard extends React.Component {
    }

조정(Alignment)

  • JSX 구문에 따른 정렬 스타일을 사용합니다.

eslint rules: react/jsx-closing-bracket-location.

```javascript
// bad
<Foo superLongParam="bar"
     anotherSuperLongParam="baz" />

// good
<Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
/>

// if props fit in one line then keep it on the same line
<Foo bar="bar" />

// children get indented normally
<Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
>
  <Spazz />
</Foo>
```

인용(Quotes)

  • JSX 속성(attributes)에는 항상 큰 따옴표(")를 사용합니다. 그러나 다른 모든 자바스크립트에는 작은 따옴표(single quotes)를 사용합니다.

왜죠? JSX 속성(attributes)은 따옴표(quotes)의 탈출(escaped)을 포함할 수 없습니다. 그래서 큰 따옴표를 이용하여 "don't"와 같은 접속사를 쉽게 입력할 수 있습니다. 일반적으로 HTML 속성(attributes)에는 작은 따옴표 대신 큰 따옴표를 사용합니다. 그래서 JSX 속성역시 동일한 규칙이 적용됩니다.

eslint rules: jsx-quotes.

```javascript
// bad
<Foo bar='bar' />

// good
<Foo bar="bar" />

// bad
<Foo style={{ left: "20px" }} />

// good
<Foo style={{ left: '20px' }} />
```

공백(Spacing)

  • 자신을 닫는(self-closing) 태그에는 항상 하나의 공백만을 사용합니다.

    // bad
    <Foo/>
    
    // very bad
    <Foo                 />
    
    // bad
    <Foo
     />
    
    // good
    <Foo />

속성(Props)

  • prop 이름은 항상 camelCase(소문자로 시작)를 사용합니다.

    // bad
    <Foo
      UserName="hello"
      phone_number={12345678}
    />
    
    // good
    <Foo
      userName="hello"
      phoneNumber={12345678}
    />
  • 명시적으로 true 값을 가지는 prop은 그 값을 생략할 수 있습니다.

eslint rules: react/jsx-boolean-value.

```javascript
// bad
<Foo
  hidden={true}
/>

// good
<Foo
  hidden
/>
```

괄호(Parentheses)

  • JSX 태그가 감싸여(Wrap) 있어 한 줄 이상인 경우 괄호(parentheses)를 사용합니다.

eslint rules: react/wrap-multilines.

```javascript
// bad
render() {
  return <MyComponent className="long body" foo="bar">
           <MyChild />
         </MyComponent>;
}

// good
render() {
  return (
    <MyComponent className="long body" foo="bar">
      <MyChild />
    </MyComponent>
  );
}

// good, when single line
render() {
  const body = <div>hello</div>;
  return <MyComponent>{body}</MyComponent>;
}
```

태그(Tags)

  • 자식(children)을 가지지 않는다면 항상 자신을 닫는(self-close) 태그로 작성합니다.

eslint rules: react/self-closing-comp.

```javascript
// bad
<Foo className="stuff"></Foo>

// good
<Foo className="stuff" />
```
  • 만약, 컴포넌트의 속성(properties)을 여러 줄에 있는 경우, 닫는 태그는 다음 줄에 작성합니다.

eslint rules: react/jsx-closing-bracket-location.

```javascript
// bad
<Foo
  bar="bar"
  baz="baz" />

// good
<Foo
  bar="bar"
  baz="baz"
/>
```

메소드(Methods)

  • 렌더러 메소드에서 이벤트 핸들러에 바인드(Bind)가 필요한 경우에는 생성자(constructor)에서 합니다.

왜죠? 렌더러 메소드에서 바인드(bind)를 호출하게 되면 랜더링 할 때 마다 매번 새로운 함수를 생성하게 됩니다.

eslint rules: react/jsx-no-bind.

```javascript
// bad
class extends React.Component {
  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv.bind(this)} />
  }
}

// good
class extends React.Component {
  constructor(props) {
    super(props);

    this.onClickDiv = this.onClickDiv.bind(this);
  }

  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv} />
  }
}
```
  • React 컴포넌트의 내부 메소드에 밑줄(underscore)을 접두사로 사용하지 않습니다.

    // bad
    React.createClass({
      _onClickSubmit() {
        // do stuff
      },
    
      // other stuff
    });
    
    // good
    class extends React.Component {
      onClickSubmit() {
        // do stuff
      }
    
      // other stuff
    }

호출순서(Ordering)

  • class extends React.Component의 호출순서(Ordering):
  1. constructor
  2. 추가적인(optional) static 메소드
  3. getChildContext
  4. componentWillMount
  5. componentDidMount
  6. componentWillReceiveProps
  7. shouldComponentUpdate
  8. componentWillUpdate
  9. componentDidUpdate
  10. componentWillUnmount
  11. onClickSubmit()와 같은 clickHandlers 또는 eventHandlers 또는 onChangeDescription()
  12. getSelectReason()와 같은 render를 위한 getter methods 또는 getFooterContent()
  13. renderNavigation()와 같은 추가적인 렌더러 메소드 또는 renderProfilePicture()
  14. render
  • propTypes, defaultProps, contextTypes, 등을 어떻게 정의할까요...

    import React, { PropTypes } from 'react';
    
    const propTypes = {
      id: PropTypes.number.isRequired,
      url: PropTypes.string.isRequired,
      text: PropTypes.string,
    };
    
    const defaultProps = {
      text: 'Hello World',
    };
    
    class Link extends React.Component {
      static methodsAreOk() {
        return true;
      }
    
      render() {
        return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>
      }
    }
    
    Link.propTypes = propTypes;
    Link.defaultProps = defaultProps;
    
    export default Link;
  • React.createClass의 호출순서(Ordering):

  1. displayName
  2. propTypes
  3. contextTypes
  4. childContextTypes
  5. mixins
  6. statics
  7. defaultProps
  8. getDefaultProps
  9. getInitialState
  10. getChildContext
  11. componentWillMount
  12. componentDidMount
  13. componentWillReceiveProps
  14. shouldComponentUpdate
  15. componentWillUpdate
  16. componentDidUpdate
  17. componentWillUnmount
  18. onClickSubmit()와 같은 clickHandlers 또는 eventHandlers 또는 onChangeDescription()
  19. getSelectReason()와 같은 render를 위한 getter methods 또는 getFooterContent()
  20. renderNavigation()와 같은 추가적인 렌더러 메소드 또는 renderProfilePicture()
  21. render

eslint rules: react/sort-comp.

isMounted

  • isMounted는 사용하지 않습니다.

왜죠? isMounted는 안티-패턴(anti-pattern)입니다. ES6 클래스에서는 사용할수도 없습니다. 그리고 공식적으로 사용되지 않게(deprecated) 될 것입니다.

eslint rules: react/no-is-mounted.

⬆ 목록으로