使用 ES6 class 声明,可不严格遵守该属性声明次序
但如有 propTypes 则必须写在顶部, lifecycle events 必须写到一起
数组遍历,map,必须要用 key属性
在一个函数里面,改变传入的对象本身是不好的写法

  • propTypes
  • defaultPropTypes
  • constructor
    • event handlers (如不使用类属性语法可在此声明)
  • lifecycle events
  • event handlers
  • getters
  • render

    1. class App extends React.Component {
    2. static propTypes = {
    3. firstName: PropTypes.string.isRequired,
    4. lastName: PropTypes.string.isRequired
    5. }
    6. static defaultPropTypes = {
    7. firstName: '',
    8. lastName: '',
    9. }
    10. constructor(props) {
    11. super(props)
    12. this.state = { visible: false }
    13. /* 若不能使用 babel-plugin-transform-class-properties
    14. this.handleClick = () => {
    15. this.setState({visible: !this.state.visible})
    16. }
    17. */
    18. }
    19. componentWillMount() { }
    20. componentDidMount() { }
    21. handleClick = () => {
    22. this.setState({ visible: !this.state.visible })
    23. }
    24. get fullName() {
    25. const { firstName, lastName } = this.props
    26. return `${firstName} ${lastName}`
    27. }
    28. render() {
    29. return (
    30. <div onClick={this.handleClick}>
    31. {this.fullName} {this.state.visible ? 'is visible' : ''}
    32. </div>
    33. )
    34. }
    35. }

get计算属性

使用 getters 封装 render 所需要的状态或条件的组合
对于返回 boolean 的 getter 使用 is- 前缀命名,例如 isVisible

  1. class App extends React.Component {
  2. get isVIP() {
  3. const { age } = this.state
  4. const { level } = this.props
  5. const vips = ['A', 'B']
  6. return age > 18 && (vips.includes(level))
  7. }
  8. render() {
  9. return (
  10. <div>
  11. {this.isVIP ? <VipComponent /> : <NormalComponent />}
  12. </div>
  13. )
  14. }
  15. }

不推荐的用法

  1. // bad
  2. render () {
  3. return (
  4. <div>
  5. {
  6. this.state.age > 18
  7. && (this.props.level === 'A'
  8. || this.props.level === 'B')
  9. ? <VipComponent />
  10. : <NormalComponent />
  11. }
  12. </div>
  13. )
  14. }

事件回调命名规范

Handler 命名风格:

  • 使用 handle 开头,使用一般现在时
  • 以事件类型作为结尾 (如 Click, Change)
  • handleClick,handleChange ```jsx closeAll = () => {},

// good render () { return

}

// bad render () { return

}

  1. 如果你需要区分同样事件类型的 handler(如 `handleNameChange` `handleEmailChange`)时,<br />可能这就是一个拆分组件的信号
  2. <a name="lLQjn"></a>
  3. ## 组件化代替多个render
  4. 当组件的 jsx 只写在一个 render 方法显得太臃肿时,就拆分出一个个小组件<br />采用 class component stateless component 无状态组件
  5. Table表格组件,用的 render方法比较多
  6. - renderAction
  7. - renderStatus
  8. - RenderTime
  9. ```jsx
  10. class App extends React.Component {
  11. render() {
  12. return (
  13. <div className="menu">
  14. <ul>
  15. {this.props.items.map(item => <Items {...item} />)}
  16. </ul>
  17. </div>
  18. )
  19. }
  20. }
  21. // Items子组件负责渲染逻辑
  22. function Items ({name}) {
  23. return (
  24. <li>
  25. {name}
  26. {/* ... */}
  27. </li>
  28. )
  29. }

不推荐的写法

  1. // bad
  2. class App extends React.Component {
  3. renderItem({ name }) {
  4. return (
  5. <li>
  6. {name}
  7. {/* ... */}
  8. </li>
  9. )
  10. }
  11. render() {
  12. return (
  13. <div className="menu">
  14. <ul>
  15. {this.props.items.map(item => this.renderItem(item))}
  16. </ul>
  17. </div>
  18. )
  19. }
  20. }

state状态提升

state状态上移优于公共方法
把公共数据放在父组件里面
Lifting State Up https://reactjs.org/docs/lifting-state-up.html

更细颗粒的组件化,状态应集中在远离渲染的地方处理,

  • 比如应用级别的状态就在 redux 的 store 里
  • 也能使兄弟组件更方便地共享
  • 一般组件不应提供公共方法,这样会破坏数据流只有一个方向的原则 ```jsx // parent import DropDownMenu from ‘./DropDownMenu’

class App extends React.Component { constructor (props) { super(props) this.state = { visible: false } }

showMenu () { this.setState({visible: true}) }

hideMenu () { this.setState({visible: false}) }

render () { return } }

  1. child子组件
  2. ```jsx
  3. class DropDownMenu extends Component {
  4. static propsType = {
  5. visible: PropTypes.boolean.isRequired
  6. }
  7. render () {
  8. if(!this.props.visible) return null
  9. return (
  10. <div className="dropdown-menu">
  11. {/* ... */}
  12. </div>
  13. )
  14. }
  15. }

不推荐的写法

  1. class App extends React.Component {
  2. showMenu () {
  3. this.refs.menu.show()
  4. }
  5. hideMenu () {
  6. this.refs.menu.hide()
  7. }
  8. render () {
  9. return <DropDownMenu ref="menu" />
  10. }
  11. }
  12. // child 子组件
  13. class DropDownMenu extends Component {
  14. constructor (props) {
  15. super(props)
  16. this.state = {
  17. visible: false
  18. }
  19. }
  20. show () {
  21. this.setState({visible: true})
  22. }
  23. hide () {
  24. this.setState({visible: false})
  25. }
  26. render () {
  27. return this.state.visible && (
  28. <div className="dropdown-menu">
  29. {/* ... */}
  30. </div>
  31. )
  32. }
  33. }

容器组件

容器组件主要负责 维护状态和 数据的计算

  • 本身并没有界面逻辑,只把结果通过 props 传递下去
  • 组件的状态和渲染解耦开来
  • 改写界面时不用关注数据的实现,顺便得到了可复用性 ```jsx class MessageContainer extends Component { constructor(props) { super(props) this.state = {

    1. onlyUnread: false,
    2. messages: []

    } }

    // 父组件获取数据,处理副作用 componentDidMount() { $.ajax({

    1. url: "/api/messages",

    }).then(({ messages }) => this.setState({ messages })) }

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

    render() { return <MessageList

    1. messages={this.state.messages.filter(msg => this.state.onlyUnread ? !msg.asRead : true)}
    2. toggleUnread={this.handleClick}

    /> } }

// child子组件 MessageList.propTypes = { messages: propTypes.array.isRequired, toggleUnread: propTypes.func.isRequired }

function MessageList({ messages, toggleUnread }) { return (

    { messages.map(({ content, author }) => { return
  • {content}—{author}
  • }) }
) }

  1. bad,不推荐的写法,<br />组件的状态和渲染没有分开
  2. ```jsx
  3. class MessageList extends Component {
  4. constructor (props) {
  5. super(props)
  6. this.state = {
  7. onlyUnread: false,
  8. messages: []
  9. }
  10. }
  11. componentDidMount () {
  12. $.ajax({
  13. url: "/api/messages",
  14. }).then(({messages}) => this.setState({messages}))
  15. }
  16. handleClick = () => this.setState({onlyUnread: !this.state.onlyUnread})
  17. render () {
  18. return (
  19. <div class="message">
  20. <ul>
  21. {
  22. this.state.messages
  23. .filter(msg => this.state.onlyUnread ? !msg.asRead : true)
  24. .map(({content, author}) => {
  25. return <li>{content}—{author}</li>
  26. })
  27. }
  28. </ul>
  29. <button onClick={this.handleClick}>toggle unread</button>
  30. </div>
  31. )
  32. }
  33. }