React模式【譯】

原文:react-patternshtml

代碼結構

  • class definitionreact

    • constructorgit

      • event handlersgithub

    • 'component' lifecycleajax

    • gettersnpm

    • renderjson

  • defaultProps數組

  • proptypes瀏覽器

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

    this.state = { smiling: false };

    this.handleClick = () => {
      this.setState({smiling: !this.state.smiling});
    };
  }

  componentWillMount () {
    // add event listeners (Flux Store, WebSocket, document, etc.)
  },

  componentDidMount () {
    // React.getDOMNode()
  },

  componentWillUnmount () {
    // remove event listeners (Flux Store, WebSocket, document, etc.)
  },

  get smilingMessage () {
    return (this.state.smiling) ? "is smiling" : "";
  }

  render () {
    return (
      <div onClick={this.handleClick}>
        {this.props.name} {this.smilingMessage}
      </div>
    );
  },
}

Person.defaultProps = {
  name: 'Guest'
};

Person.propTypes = {
  name: React.PropTypes.string
};

格式化屬性

當有2個以上屬性時,就換行顯示緩存

// bad
<Person
 firstName="Michael" />

// good
<Person firstName="Michael" />
// bad
<Person firstName="Michael" lastName="Chan" occupation="Designer" favoriteFood="Drunken Noodles" />

// good
<Person
 firstName="Michael"
 lastName="Chan"
 occupation="Designer"
 favoriteFood="Drunken Noodles" />

依賴屬性

使用getters方法代替定義依賴屬性

// bad
  firstAndLastName () {
    return `${this.props.firstName} ${this.props.lastname}`;
  }

  // good
  get fullName () {
    return `${this.props.firstName} ${this.props.lastname}`;
  }

依賴狀態

使用getters方法代替定義依賴狀態,注意爲了提升可讀性須要在命名時加上一個動詞前綴。

// bad
happyAndKnowsIt () {
  return this.state.happy && this.state.knowsIt;
}
// good
get isHappyAndKnowsIt () {
  return this.state.happy && this.state.knowsIt;
}

這些方法必須返回boolean類型

用三元運算代替Sub-render方法

保證渲染邏輯都寫在render方法裏

// bad
renderSmilingStatement () {
  return <strong>{(this.state.isSmiling) ? " is smiling." : ""}</strong>;
},

render () {
  return <div>{this.props.name}{this.renderSmilingStatement()}</div>;
}
// good
render () {
  return (
    <div>
      {this.props.name}
      {(this.state.smiling)
        ? <span>is smiling</span>
        : null
      }
    </div>
  );
}

視圖組件

用定義的組件組成視圖。不要建立混雜着佈局和功能的一次性組件

// bad
class PeopleWrappedInBSRow extends React.Component {
  render () {
    return (
      <div className="row">
        <People people={this.state.people} />
      </div>
    );
  }
}
// good
class BSRow extends React.Component {
  render () {
    return <div className="row">{this.props.children}</div>;
  }
}

class SomeView extends React.Component {
  render () {
    return (
      <BSRow>
        <People people={this.state.people} />
      </BSRow>
    );
  }
}

容器組件(有狀態組件)

容器組件負責獲取數據並交付給相應的子組件渲染,僅此而已。— Jason Bonta

// bad
// CommentList.js

class CommentList extends React.Component {
  getInitialState () {
    return { comments: [] };
  }

  componentDidMount () {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments});
      }.bind(this)
    });
  }

  render () {
    return (
      <ul>
        {this.state.comments.map(({body, author}) => {
          return <li>{body}—{author}</li>;
        })}
      </ul>
    );
  }
}
//good
// CommentList.js

class CommentList extends React.Component {
  render() {
    return (
      <ul>
        {this.props.comments.map(({body, author}) => {
          return <li>{body}—{author}</li>;
        })}
      </ul>
    );
  }
}
// CommentListContainer.js

class CommentListContainer extends React.Component {
  getInitialState () {
    return { comments: [] }
  }

  componentDidMount () {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments});
      }.bind(this)
    });
  }

  render () {
    return <CommentList comments={this.state.comments} />;
  }
}

相關連接:
Container Components
React.js Conf 2015 - Making your app fast with high-performance components

render中緩存狀態

不要在render中緩存狀態

// bad
render () {
  let name = `Mrs. ${this.props.name}`;

  return <div>{name}</div>;
}

// good
render () {
  return <div>{`Mrs. ${this.props.name}`}</div>;
}
// best
get fancyName () {
  return `Mrs. ${this.props.name}`;
}

render () {
  return <div>{this.fancyName}</div>;
}

這裏多半是出於代碼風格的考慮,不過仍是保持比較好,我懷疑也可能跟性能有關。

複合條件

不要把複合條件判斷放在render裏。

// bad
render () {
  return <div>{if (this.state.happy && this.state.knowsIt) { return "Clapping hands" }</div>;
}
// better
get isTotesHappy() {
  return this.state.happy && this.state.knowsIt;
},

render() {
  return <div>{(this.isTotesHappy) && "Clapping hands"}</div>;
}

這裏最好的解決方案是使用容器組件來管理你的狀態而後在經過屬性(props)往下層傳遞。

檢查空值

不要去檢查是否存在某個prop值,快使用defaultProps

// bad
render () {
  if (this.props.person) {
    return <div>{this.props.person.firstName}</div>;
  } else {
    return null;
  }
}
// good
class MyComponent extends React.Component {
  render() {
    return <div>{this.props.person.firstName}</div>;
  }
}

MyComponent.defaultProps = {
  person: {
    firstName: 'Guest'
  }
};

當你的值是對象或者數組時,使用 PropTypes.shape聲明嵌套數據的預期類型。

經過Props設置State

不要經過props值去設置state,除非明顯是個初始值。

// bad
getInitialState () {
  return {
    items: this.props.items
  };
}
// good
getInitialState () {
  return {
    items: this.props.initialItems
  };
}

詳細請閱讀官網的Props in getInitialState Is an Anti-Pattern

命名事件響應方法

// bad
punchABadger () { /*...*/ },

render () {
  return <div onClick={this.punchABadger} />;
}
// good
handleClick () { /*...*/ },

render () {
  return <div onClick={this.handleClick} />;
}

處理方法的命名必須:

  • 第一個單詞爲handle

  • 最後一個單詞爲要響應的事件(好比Click,Change)

  • 如今時態
    若是爲了不命名衝突,你能夠在handle和事件名中間加入其餘信息。好比,你能夠定義handleNameChange 和handleAgeChange來區分onChange的不一樣響應處理。不過當你這樣作的時候,你要問問本身是否須要一個新的組件了。

命名事件

可使用自定義事件替代預設的事件名。

class Owner extends React.Component {
  handleDelete () {
    // handle Ownee's onDelete event
  }

  render () {
    return <Ownee onDelete={this.handleDelete} />;
  }
}

class Ownee extends React.Component {
  render () {
    return <div onChange={this.props.onDelete} />;
  }
}

Ownee.propTypes = {
  onDelete: React.PropTypes.func.isRequired
};

使用PropTypes

使用PropTypes能夠預先定義屬性的類型,能夠在以後得到一些有意義的警告信息。

MyValidatedComponent.propTypes = {
  name: React.PropTypes.string
};

MyValidatedComponentname屬性值若是不是string類型的話, 會輸出警告。

<Person name=1337 />
// Warning: Invalid prop `name` of type `number` supplied to `MyValidatedComponent`, expected `string`.

在這裏也能夠設置屬性是不是必須存在的。

MyValidatedComponent.propTypes = {
  name: React.PropTypes.string.isRequired
}

這個組件會驗證是否存在name屬性。

<Person />
// Warning: Required prop `name` was not specified in `Person`

相關連接:Prop Validation

使用特殊符號

要在使用React中使用特殊符號,請使用String.fromCharCode()

// bad
<div>PiCO · Mascot</div>

// nope
<div>PiCO &middot; Mascot</div>

// good
<div>{'PiCO ' + String.fromCharCode(183) + ' Mascot'}</div>

// better
<div>{`PiCO ${String.fromCharCode(183)} Mascot`}</div>

相關連接:HTML Entities

Tables

瀏覽器認爲你是愚蠢的,可是React不這麼人爲。請始終爲你的table組件添加tbody。

// bad
render () {
  return (
    <table>
      <tr>...</tr>
    </table>
  );
}

// good
render () {
  return (
    <table>
      <tbody>
        <tr>...</tr>
      </tbody>
    </table>
  );
}

瀏覽器會自動插入tbody當你忘了寫它。React則不會,這樣會給你的代碼帶來混亂,請記住使用tbody。

classnames

使用classNames管理你的classes邏輯。

// bad
get classes () {
  let classes = ['MyComponent'];

  if (this.state.active) {
    classes.push('MyComponent--active');
  }

  return classes.join(' ');
}

render () {
  return <div className={this.classes} />;
}
// good
render () {
  let classes = {
    'MyComponent': true,
    'MyComponent--active': this.state.active
  };

  return <div className={classnames(classes)} />;
}

相關連接:Class Name Manipulation

相關文章
相關標籤/搜索