current position:Home>Deep understanding of children https://segmentfault.com/a/1190000011527160

Deep understanding of children https://segmentfault.com/a/1190000011527160

2022-04-29 04:26:18baoleilei6

React The core component is . You can nest like HTML Use these components nested like tags , This makes writing JSX Easier because it's similar to markup language .

When I just started to learn React when , At that time, I thought “ Use  props.children  That's it , I know everything about it ”. I was wrong ..

Because of what we use JavaScript, We will change children. We can send them special attributes , This determines whether to render them . Let's explore React in children The role of .

Child components

We have a component  <Grid />  Contains several components  <Row /> . You might use it like this :

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

These three  Row  Components have become  Grid  Of  props.children . Use an expression container , Parent components can render their child components :

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

The parent component can also decide not to render any child components or operate on them before rendering . for example , This  <Fullstop />  Component does not render its subcomponents :

class Fullstop extends React.Component {
  render() {
    return <h1>Hello world!</h1>
  }
}

No matter what subcomponent you pass to this component , It will only show “Hello world!”

Anything can be a child

React Medium Children Not necessarily components , They can make anything . for example , We can use the above text as children Pass on our  <Grid />  Components .

<Grid>Hello world!</Grid>

JSX The spaces at the beginning and end of each line will be automatically deleted , And blank lines . It also compresses the blank line in the middle of the string into a space .

This means that the following examples will render the same situation :

<Grid>Hello world!</Grid>

<Grid>
  Hello world!
</Grid>

<Grid>
  Hello
  world!
</Grid>

<Grid>

  Hello world!
</Grid>

You can also put many types of children Perfect combination :

<Grid>
  Here is a row:
  <Row />
  Here is another row:
  <Row />
</Grid>

child The function of

We can convey any JavaScript Expression as children, Include function .

To illustrate this situation , Here is a component , It will execute a passed as child Function of :

class Executioner extends React.Component {
  render() {
    // See how we're calling the child as a function?
    //                        ↓
    return this.props.children()
  }
}

You will use this component like this

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

Of course , This example is of no use , Just showing the idea .

Suppose you want to get some data from the server . You can do this in many ways , Use functions like this as child The method is also feasible .

<Fetch url="api.myself.com">
  {(result) => <p>{result}</p>}
</Fetch>

Don't worry that this is beyond your brain capacity . What I want is not to be surprised when you encounter this situation in the future . With children Anything can happen .

operation children

If you've seen it React You'll say “children Is an opaque data structure ”. essentially , props.children  Can make any type , For example, array 、 function 、 Objects, etc. .

React A series of function assistants are provided to make the operation children It is more convenient .

loop

The two most prominent function assistants are  React.Children.map  as well as  React.Children.forEach . They work in the case of corresponding arrays , besides , Function as 、 Object or anything as children On delivery , They also work .

class IgnoreFirstChild extends React.Component {
  render() {
    const children = this.props.children
    return (
      <div>
        {React.Children.map(children, (child, i) => {
          // Ignore the first child
          if (i < 1) return
          return child
        })}
      </div>
    )
  }
}

<IgnoreFirstChild />  The component will traverse all children, Ignore the first child Then return to the other .

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

under these circumstances , We can also use  this.props.children.map  Methods . But if someone talks about a function as child What will happen if you pass it on ?this.props.children  It will be a function, not an array , Then we will have a error!

However, using  React.Children.map  function , No matter what will not be reported wrong .

<IgnoreFirstChild>
  {() => <h1>First</h1>} // <- Ignored ?
</IgnoreFirstChild>

Count

because this.props.children  It can be of any kind , Check how many... There are in a component children It's very difficult . Naive use  this.props.children.length , When a string or function is passed, the program breaks . Suppose we have a child:"Hello World!" , But use  .length  The method will be displayed as 12.

That's why we have  React.Children.count  Reason for method

class ChildrenCounter extends React.Component {
  render() {
    return <p>React.Children.count(this.props.children)</p>
  }
}

No matter what type it is, it will return children The number of

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

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

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

Convert to array

If none of the above methods are suitable for you , You can children Convert to array through  React.Children.toArray  Method . If you need to sort them , This method is very useful .

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>

The above example will be rendered as three ordered strings .

Single execution child

If you come back and think about what just happened  <Executioner />  Components , It can only pass a single child In case of use , and child Must be a function .

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

We can try to enforce  propTypes , It looks like this

Executioner.propTypes = {
  children: React.PropTypes.func.isRequired,
}

This causes the console to print a message , Some developers will ignore it . Contrary , We can use it in  render  It uses  React.Children.only

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

Only one will return child. If more than one child, It will throw an error , Put the whole program into interruption —— It perfectly avoids lazy developers trying to destroy components .

edit children

We can render any component as children, But you can still use the parent component to control them , Instead of using rendered components . To illustrate that , Let's take an example Can have a lot of  RadioButton  Component's  RadiaGroup  Components .

RadioButtons  Not from  RadioGroup  Render on itself , They are only used as children Use . This means that we will have such code .

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

There's a problem with this code .input  Not grouped , This led to :

In order to  input  Get the label in the same group , Must have the same name  attribute . Of course, we can give it directly to everyone RadioButton  Of name  assignment

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

But this is boring and error prone . We have JavaScript All the functions of !

change children Properties of

stay RadioGroup  We will add one called  renderChildren  Methods , Here we edit children Properties of

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

  renderChildren() {
    // TODO: Change the name prop of all children
    // to this.props.name
    return this.props.children
  }

  render() {
    return (
      <div className="group">
        {this.renderChildren()}
      </div>
    )
  }
}

Let's start traversing children Get each child

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

How do we edit their properties ?

Clone elements forever

This is the last auxiliary method shown today . seeing the name of a thing one thinks of its function ,React.cloneElement  Will clone an element . We take the element we want to clone as the first parameter , Then take the attribute you want to set as the second parameter in the form of an object .

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

Now? ,clone  The element is set to  "yes!"  Properties of  new

This is exactly what we have  RadioGroup  The required . We clone all child And set up name  attribute

renderChildren() {
  return React.Children.map(this.props.children, child => {
    return React.cloneElement(child, {
      name: this.props.name
    })
  })
}

The last step is to pass a unique  name  to RadioGroup

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

No manual addition  name  Attribute to all  RadioButton , We just told  RadioGroup  The required name nothing more .

summary

Children send React Components are more like tags than Disjointed entities . Through the powerful JavaScript And some of the React Help function makes our life easier .

copyright notice
author[baoleilei6],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/117/202204270552176287.html

Random recommended