组件数据

React 中三种数据应用类型

  1. Props
  2. State
  3. Context

Props

可以是任何类型

props 就是属性 prototype 的缩写,在 JSX 中 props 就像 HTML 的 attribute 一样。在概念上 props 对于组件,就相当于 js 当中的参数对应函数一样。组件函数接收 props 作为参数,然后返回视图的内容。props 比起原生的 html 属性强大许多,可以传入变量、函数、组件 ...

//Function
Component(props) = View

//JSX
const SimpleButton = props => <button className={props.color}>Submit</button>
ReactDOM.render(<SimpleButton color="blue"/>,document.getElementById('app'))

//HTML
<button class="blue">Submit</button>

自上向下传递,自读不可修改

Props 的值自上向下传递,从父组件传入到子组件当中,并且 props 是自读的,不能在组件内部修改 props 的内容。

const Children = props => <p>{props.name}</p>
const Father = <Children name="Bob"/>

拥有类型检查和默认值

Props 有类型检查和默认值,通过 prop-types 设置。

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

const Title = props => <h1>{props.title}</h1>

Title.defaultProps = {
  title: 'Hello world!'
}

Title.propTypes = {
  title: PropTypes.string.isRequired
}

ReactDOM.render(<Title/>,document.getElementById('root'))

State

state 翻译过来可以为状态,一个组件可以有自身的状态,也可以包含整个 react 应用的状态。

  • 初始化
  • setState方法设置
  • 向下传递数据
  • 多个组件共用的 State 存放在其公有的父组件里

简单的计数器例子

  1. 通过 class 类定义组件来声明一个有状态组件
  2. 然后在构造方法中初始化状态的 state 并为它赋默认值
  3. 再添加一个负责处理 state 变化的函数 addOne
  4. 通过 setState 的方法来改变 state 值。
import React from 'react'
import ReactDOM from 'react-dom'

class Counter extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      counter:0
    }
  }
    
  addOne() {
    this.setState({
      counter:this.state.counter +1
    })
  }
    
  render() {
    return(
      <div>
        <p>{this.state.counter}</p>
          <button onClick={()=>this.addOne()}>Increment</button>
      </div>
    )
  }
}

ReactDOM.render(<Counter />,document.getElementById('root'))

复杂点的点赞例子,通过点击切换星星状态。

import React from 'react'
import ReactDOM from 'react-dom'

const Title = props => <h1>{props.title}</h1>
const UserInfo = props => <p>{props.firstName + ' ' + props.lastName}</p>

class LikeButton extends React.Component {
  constructor(props) {
    super(props)
  }
    
  render() {
    return <button onClick={this.props.handleClick}>{this.props.mark}</button>
  }
}

class App extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      mark:'☆'
    }
  }
    
  handleButtonClick(){
    const mark = (this.state.mark === '☆') ? '★' : '☆';
    this.setState({
      mark:mark
    })
  }
    
  render() {
    return (
      <div>
        <Title title="Hello , jikexueyuan"/>
        <UserInfo firstName="Jax" lastName="Chu" />
        <LikeButton mark={this.state.mark} handleClick={this.handleButtonClick.bind(this)} />
      </div>
    )
  }
}

const Root = props => <div className="container">{props.children}</div>

ReactDOM.render(
  <Root>
    <App/>
  </Root>
  ,document.getElementById('root'))

假如 State 分散到应用的各个部分,有的复杂后端数据的交互,有的处理用户输入,有的负责页面变化的逻辑,后期维护是相当困难的。因此在开发 React应用中,我们应尽量把 State统一管理,通过 props 把 state 的数据传递到组件当中。

例子:货币汇率转换器

import React from 'react'
import ReactDOM from 'react-dom'

class CurrencyConvertor extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      money : 100
    }
  }
    
  handleInputChange(event) {
    this.setState({
      money:event.target.value
    })
  }
    
  render() {
    return (
      <div>
        <h2>货币兑换</h2>
        <YuanInput money={this.state.money} handleChange={(e)=> this.handleInputChange(e)} />
        <MoneyCovertor type='美元' unit='dollar' money={this.state.money} rate={0.1453} />
        <MoneyCovertor type='日元' unit='yen' money={this.state.money} rate={16.1814} />
      </div>
    )
  }
}

class YuanInput extends React.Component {
  constructor(props){
    super(props)
  }
    
  handleChange(event){
    this.props.handleChange(event)
  }

  render() {
    return (
      <p>
        <lable>
        人民币
        <input name='yuan' onChange={(e)=>this.handleChange(e)} value={this.props.money} />
        </lable>
      </p>
    )
  }
}

const MoneyCovertor = props => (
  <p>
    <lable>
      {props.type}
      <input name={props.unit} value={(props.money * props.rate).toFixed(2)} disabled />
    </lable>
  </p>
)

ReactDOM.render(<CurrencyConvertor /> ,document.getElementById('root'))

Context

集中管理 state 会引发另一个问题,state 的数据都是自上而下传递的,假如我们在最外一层进行组件管理,而我们使用的子组件确在N层之下,那么我们就需要在每一层的组件都传递 props 下去。所以 React提供 Context 带有实验性质,用于跨层级传递数据。

Context 必须进行类型检查。

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

const UserInfo = (props,context) => {
  console.log(props,context)
  return(
    <div className="UserInfo-name">{context.lastName +','+ props.firstName}</div>
  )
}

UserInfo.contextTypes = {
    lastName:PropTypes.string
}

const Column = props => <div className='column'><UserInfo firstName={props.firstName} /></div>
const Row    = props => <div className='row'><Column firstName={props.firstName} /></div>
const Container = props => <div className='container'><Row firstName={props.firstName} /></div>

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      firstName: 'Chu',
    }
  }
    
  getChildContext() {
    return {
      lastName: 'Jax'
    }
  }
    
  render() {
    return (
      <div className="app">
        <Container firstName={this.state.firstName} />
      </div>
    )
  }
}

App.childContextTypes = {
  lastName: PropTypes.string
}

ReactDOM.render(<App/>,document.getElementById('root'))