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即可以实现功能复制代码
相关文章
相关标签/搜索