Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React组件编写思路(一) #7

Open
sixwinds opened this issue Nov 7, 2016 · 0 comments
Open

React组件编写思路(一) #7

sixwinds opened this issue Nov 7, 2016 · 0 comments

Comments

@sixwinds
Copy link
Owner

sixwinds commented Nov 7, 2016

React组件编写思路(一)

新手写 React 组件往往无从入手,怎么写,什么时候用 props,什么时候用 state 摸不着头脑。其实是没有了解到 React 的一些思想。就我个人的经验大多数的组件都有一定的套路可言,接下来就先介绍下 React 组件的基本思想。

React 组件可以分为可控组件和非可控组件。可控组件意思是组件自身控制自己的状态(属性),可以通过自身提供的方法(供调用者使用)来改变自己的状态。譬如一个 input text 输入框提供一个 reset 方法,如果要清空用户输入则通过获得 inupt 组件对象,然后调用 reset 方法来做

refs.inputRef.rest() 

非可控组件的意思是组件本身的状态(属性)自己无法更改,只能随着外部传入的值(props)而变化。还是拿输入框清空这一个操作来说,非可控的 input 不通过自己提供方法来改变(维护)自己的状态(value),只通过外部传入一个值为空字符串的 value 来做到清空的效果。

reset(){
  this.setState({
    inputValue: ''
  })
}
render(){
  return <input value={this.state.inputValue}/>
}

我们拿一个场景来看下完整的代码(一个 form 中有一个 input,有一个 reset 按妞,点击 reset 按妞会清空用户的输入),看下这两种组件书写的区别。
受控组件例:

class App extends React.Component {
  reset = ()=>{
    this.refs.myInput.reset() // 假设 input 有一个 reset 方法
  }
  render() {
    <div>
      <form>
        <input type="text"  ref="myInput" />
        <button onClick={ this.reset }>Reset</button>
      </form>
    </div>
  }
}

非受控组件例:

class App extends React.Component {
  constructor( props ){
    super( props );
    this.state = {
      inputValue: 'Plz input your text.'
    }
  }
  reset = ()=>{
    this.setState( {
      inputValue: ''
    } )
  }
  render() {
    <div>
      <form ref="myForm">
        <input type="text" value={ this.state.inputValue }/>
        <button onClick={ this.reset }>Reset</button>
      </form>
    </div>
  }
}

接下来我们来看下如果编写这两种组件,打个比方我们要自定义一个 alert 组件。我们先从非受控组件说起,因为较简单。非受控组件所要做的就是把所有状态提取到组件的 props 中去,render 中就用 props。一个 alert 有哪些最基本的状态(属性)呢?我们以最基础的功能定出一个表示显示与否的 show,一个表示显示内容的 content。那么组件代码如下。

class Alert extends React.Component {
  constructor( props ) {
    super( props )
  }
  render() {
    let style = {
      display: this.props.show ? 'fixed' : 'none'
    }
    return (
      <div class="my-alert" style={ style } >
        <div class="my-alert-tit">Alert</div>
        <div>{ this.props.content }</div>
        <div class="my-alert-footer">
          <button>确定</button>
        </div>
      </div>
    );
  }
}

Alert.propTypes = {
  show: React.PropTypes.bool,
  content: React.PropTypes.string
}

我们看到最直观的就是只需要考虑到 props 的可能取值就行,不需要关心如何改变props。而使用这个非可控 alert 的代码如下:

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      alertMsg: '',
      showAlert: false
    }
    this.saveHandler = ()=>{
      // ajax success
      this.setState( {
        alertMsg: 'Save successfully',
        showAlert: true
      } )
    }
  }

  render() {
    <div>
      <button onClick={ this.saveHandler }>Save</button>
      <Alert 
        content={ this.state.alertMsg }
        show={ this.state.showAlert }
      />
    </div>
  }
}

接下来我们看下可控组件的alert怎么写。可控组件通过方法来供调用者来改变组件的状态(属性)。所以暂时我们不定义 props 只定义几个方法 show(content), hide()。组件代码如下:

class Alert extends React.Component {
  constructor( props ) {
    super( props )
    this.state = {
      content: '',
      show: false
    }
    this.show = ( content )=>{
      this.setState( {
        content: content,
        show: true
      } )
    }

    this.hide = ()=>{
      this.setState( {
        show: false
      } )
    }
  }
  render() {
    let style = {
      display: this.state.show ? 'fixed' : 'none'
    }
    return (
      <div class="my-alert" style={ style } >
        <div class="my-alert-tit">Alert</div>
        <div>{ this.state.content }</div>
        <div class="my-alert-footer">
          <button onClick={ this.hide }>确定</button>
        </div>
      </div>
    );
  }
}

我们看到可控组件内部需要用到 state 来自己改变自己的状态。使用这个可控 alert 的代码如下:

import { Alert } from 'Alert';

class App extends React.Component {
  constructor() {
    super();
    this.saveHandler = ()=>{
      // ajax success
      this.refs.myAlert.show( 'Save Successfully' );
    }
  }

  render() {
    <div>
      <button onClick={ this.saveHandler }>Save</button>
      <Alert ref="myAlert"/>
    </div>
  }
}

但是可控组件有一个问题就是他的初始化状态如何设置(如何由外部定义组件 state 的初始化值)?由于没有 props 那么只能通过方法来设置,那么这么做法很别扭。这时可以通过定义 props 把初始化状态在生成这个组件时传入,而不必等组件生成完再通过调用方法传入。于是修改后的代码如下:

class Alert extends React.Component {
  constructor( props ) {
    super( props )
    this.state = {
      content: this.props.defaultContent,
      show: this.props.defaultShow
    }
    this.show = ( content )=>{
      this.setState( {
        content: content,
        show: true
      } )
    }

    this.hide = ()=>{
      this.setState( {
        show: false
      } )
    }
  }

  render() {
    let style = {
      display: this.state.show ? 'fixed' : 'none'
    }
    return (
      <div class="my-alert" style={ style } >
        <div class="my-alert-tit">Alert</div>
        <div>{ this.state.content }</div>
        <div class="my-alert-footer">
          <button onClick={ this.hide }>确定</button>
        </div>
      </div>
    );
  }
}

Alert.propTypes = {
  defaultShow: React.PropTypes.bool,
  defaultContent: React.PropTypes.string
}

Alert.defaultProps = {
  defaultShow: false,
  defaultContent: ''
}

使用这个组件的代码:

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      alertMsg: '',
      showAlert: false
    }
    this.saveHandler = ()=>{
      // ajax success
      this.refs.myAlert.show( 'Save Successfully' );
    }
  }

  render() {
    <div>
      <button onClick={ this.saveHandler }>Save</button>
      <Alert ref="myAlert" defaultShow={false} defaultContent={''}/>
    </div>
  }
}

以上就是两种 React 组件的编写思路,你可以选择把你的组件编写成任意一种,那么使用者使用时也会有所不同。但是作为一个具有良好可用性的组件,不应该限制使用者的用法,那么下篇将介绍如何编写一个既可以作为可控组件,也可以作为一个非可控组件的组件写法。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant