React之不簡單的Children

1、children能夠是任何類型的值

1.1 正常dom元素express

<Grid>
  <Row />
  <Row />
  <Row />
</Grid>

class Grid extends React.Component {
  render() {
    return <div>{this.props.children}</div>
  }
}複製代碼

1.2 函數
api

<Executioner>
  {(arg) => <h1>Hello World!</h1>}
</Executioner>

class Executioner extends React.Component {
  render() {
    // See how we're calling the child as a function? // ↓ return this.props.children(arg) //由於children是函數,因此這裏確定也能夠傳參 } }複製代碼

不僅如此,children還能夠是jsx等等數組

2、操做children

2.1 React.Children.mapbash

<Executioner>
  {(arg) => <h1>Hello World!</h1>}
</Executioner>

class Executioner extends React.Component {
  render() {
    // See how we're calling the child as a function? // ↓ return this.props.children(arg) //由於children是函數,因此這裏確定也能夠傳參 } }複製代碼

像上圖咱們能夠用this.props.children操做,好比咱們當咱們傳了多個子組件以後又要怎麼渲染出來呢數據結構

若是你看過React官方文檔,你會看到說"子組件是一種不透明的數據結構"(opaque data structure)。意思就是props.children能夠是任何類型,好比array, function, object等等。由於什麼均可以傳,因此你也不能肯定傳過來了什麼東西。

<IgnoreFirstChild>
  <h1>First</h1>
  <h1>Second</h1> // <- Only this is rendered
</IgnoreFirstChild>

class IgnoreFirstChild extends React.Component {
  render() {
    const children = this.props.children
    return (
      <div>
        {this.props.children.map((child, i) => {
          // Ignore the first child
          if (i < 1) return
          return child
        })}   
        // 這裏咱們用了this.props.children.map,而且忽略到第一個元素,可是若是這裏傳進來的是上面
            例子是上圖中的函數,這裏就會報錯,由於map,使用React.Children.map則不會報錯

        {React.Children.map(children, (child, i) => {          // Ignore the first child
          if (i < 1) return
          return child
        })}
      // 這裏咱們用了React.Children.map,這是React提供的一些幫助函數
      </div>
    )
  }
}複製代碼

因此使用React.Children.map就沒有問題了,這是一種更加穩妥的方式app

<IgnoreFirstChild>
  {() => <h1>First</h1>} // <- Ignored 💪
</IgnoreFirstChild>
  //使用this.props.children會報錯,用React.Children.map不會報錯
複製代碼

2.2 React.Children.countdom

一樣因爲this.props.children類型的不肯定,咱們要判斷有多少個子組件就比較困難了。若是幼稚的使用this.props.children.length就很容易報錯了。並且,若是傳來一個子組件"Hello World!",.length會返回12!
class ChildrenCounter extends React.Component {
  render() {
    return <p>React.Children.count(this.props.children)</p>
  }
}複製代碼

// Renders "1"
<ChildrenCounter>
  Second!
</ChildrenCounter>

// Renders "2"
<ChildrenCounter>
  <p>First</p>
  <ChildComponent />
</ChildrenCounter>

// Renders "3"
<ChildrenCounter>
  {() => <h1>First!</h1>}
  Second!
  <p>Third!</p>
</ChildrenCounter>

// 每次都精準算出了子組件的個數複製代碼

2.3 React.Children.toArray
函數

當你須要把子組件轉化成數組時,能夠使用toArray這個函數ui

class Sort extends React.Component {
  render() {
    const children = React.Children.toArray(this.props.children)
    // Sort and render the children
    return <p>{children.sort().join('')}</p>   
  }
}
<Sort>
  // We use expression containers to make sure our strings
  // are passed as three children, not as one string
  {'bananas'}{'oranges'}{'apples'}
</Sort>

//輸出 apples bananas oranges

<Sort>
  // We use expression containers to make sure our strings
  // are passed as three children, not as one string
  我是內容
</Sort>

//這樣子也並不會報錯複製代碼

2.4 React.Children.only
this

若是你但願傳進來的組件只是一個,並且是個函數

咱們先借用proppropTypes來實現

class Executioner extends React.Component {
  render() {
    return this.props.children()
  }
}

Executioner.propTypes = {
  children: React.PropTypes.func.isRequired,
}
//這樣就會在console裏打印出日誌來,可是有的時候開發者很容易忽略這些消息。這個時候咱們就應該在render方
法里加入React.Children.only。
複製代碼

class Executioner extends React.Component {
  render() {
    return React.Children.only(this.props.children)()
  }
}
 //若是子組件多於一個會拋出一個錯誤,整個app會中止--絕對不會讓一些偷懶的開發搞亂咱們的組件。
複製代碼

2.5 React.cloneElement

好比咱們有一個RadioGroup組件,該組件包含一些RadioButton子組件(最終渲染成<input type="radio"> )。

RadioButtons不是由RadioGroup渲染的,它們做爲子組件傳入,這意味着咱們的代碼看起來像這樣:

render() {
  return(
    <RadioGroup>
      <RadioButton value="first">First</RadioButton>
      <RadioButton value="second">Second</RadioButton>
      <RadioButton value="third">Third</RadioButton>
    </RadioGroup>
  )
}複製代碼

可是這樣子的話RadioButton不會出如今一個RadioGroup中,因此咱們必須給全部的RadioButton加上同一個屬性name

<RadioGroup>
  <RadioButton name="g1" value="first">First</RadioButton>
  <RadioButton name="g1" value="second">Second</RadioButton>
  <RadioButton name="g1" value="third">Third</RadioButton>
</RadioGroup>

//這種方法顯然能夠,可是有點笨複製代碼

咱們能夠先用React.children改寫一下

class RadioGroup extends React.Component {
  constructor() {
    super()
    // Bind the method to the component context
    this.renderChildren = this.renderChildren.bind(this)
  }

  renderChildren() {
  return React.Children.map(this.props.children, child => {
    // TODO: Change the name prop to this.props.name
    return child
  })
}

  render() {
    return (
      <div className="group">
        {this.renderChildren()}
      </div>
    )
  }
}
  //這裏問題就是咱們仍是沒有給每一個RadioButton加上property name
複製代碼

這裏咱們用上咱們最後的一個api React.cloneElement

const cloned = React.cloneElement(element, {
  new: 'yes!'
})

第一個參數是拷貝源的React element, 第二個參數是prop object,clone之後會把這個prop object設置成屬
性給拷貝結果。cloned元素將持有名爲new的屬性,屬性值爲"yes!"複製代碼

那麼這裏咱們就來改寫上面的代碼

class RadioGroup extends React.Component {
  constructor() {
    super()
    // Bind the method to the component context
    this.renderChildren = this.renderChildren.bind(this)
  }

  renderChildren() {
    return React.Children.map(this.props.children, child => {
      return React.cloneElement(child,
        name: this.props.name
      })
    })
  }
 
  render() {
    return (
      <div className="group">
        {this.renderChildren()}
      </div>
    )
  }
}

<RadioGroup name="g1">
  <RadioButton value="first">First</RadioButton>
  <RadioButton value="second">Second</RadioButton>
  <RadioButton value="third">Third</RadioButton>
</RadioGroup>

//這樣子只須要寫個一個name即可以實現功能複製代碼
相關文章
相關標籤/搜索