# 组件通讯
多组件之间共享通讯
## props接收传递给组件的数据
传递数据:给组件标签添加属性
函数组件 props
```html
const HelloProps = (props)=>{
  console.log(props)
  return (
    <div>props:{props.name}</div>
  )
}

ReactDOM.render(<HelloProps name= "hua"/>,document.getElementById('root'))
```
类组件 this.props
```html
class HelloProps extends React.Component{
  render(){
    return (<div>props:{this.props.name},{this.props.role}</div>)
  }
}
ReactDOM.render(<HelloProps name= "hua" role="shi"/>,document.getElementById('root'))

```
## 组件特点
1. 可以是任意值
```html
class HelloProps extends React.Component{
  render(){
    console.log(this.props)
    return (<div>props:{this.props.name},{this.props.role} {this.props.tag}</div>)
  }
}
ReactDOM.render(<HelloProps name= "hua" role="shi" age = {19} 
  colors = {['red','green','blue']}
  fn={()=>console.log('我可以传函数')} 
  tag={<p>jsx</p>}

  />,document.getElementById('root'))
```
2. 只读
3. 在使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props

```html
class HelloProps extends React.Component{
  constructor(props){
    super(props)
    console.log(this.props)
  }
  render(){
    console.log(this.props)
    return (<div>props:{this.props.name},{this.props.role} {this.props.tag}</div>)
  }
}
ReactDOM.render(<HelloProps name= "hua" role="shi" age = {19} 
  colors = {['red','green','blue']}
  fn={()=>console.log('我可以传函数')} 
  tag={<p>jsx</p>}

  />,document.getElementById('root'))
```
## 通讯的三种方式
### 父组件传递给子组件
1. 父组件提供传递的state数据
2. 给子组件添加属性,值为state中的数据
3. 子组件中通过props接收父组件中传递的数据
```html
class Parent extends React.Component{
  state = {lastName:'师'}
  render(){
    return (
      <div className='parent'>
        父组件:
        <Child name = {this.state.lastName}/> 
      </div>
    )
  }
}
// 子组件
const Child = (props)=>{
  return (
    <div className='child'>
      <p>子组件接收到父组件的数据 {props.name}</p>
    </div>
  )
}

ReactDOM.render(<Parent  />,document.getElementById('root'))
```
### 子组件传递给父组件
利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数.
1. 父组件提供一个回调函数(用于接收数据)
2. 将改函数作为属性的值,传递给子组件
3. 子组件通过props调用回调函数
4. 将子组件的数据作为参数传递给回调函数
```html
class Parent extends React.Component{
  state={
    parentMsg:''
  }

  getChildMsg = data=>{
    console.log('接收到子组件传递过来的数据',data)
    this.setState({
      parentMsg:data
    })
  }
  render(){
    return (
      <div className='parent'>
        父组件:{this.state.parentMsg}
        <Child name = {this.getChildMsg}/> 
      </div>
    )
  }
}
// 子组件
class Child extends React.Component{
  state = {
    msg:"回调传递"
  }
  handleClick=()=>{
    // 
    this.props.name(this.state.msg)
  }
  render(){
    return (
      <div className='child'>
        <p>子组件发送到父组件</p><button onClick={this.handleClick}>点击</button>
      </div>
    )
  }
}

ReactDOM.render(<Parent  />,document.getElementById('root'))
```
## 兄弟组件间的通讯
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
状态提升
公共父组件职责:1.提供共享状态2.提供操作共享状态的方法
要通讯的子组件只能通过props接收状态或者操作状态的方法
```html
class Counter extends React.Component{
  state={
    count:0
  }
  onIncrement=()=>{
    this.setState({
      count:this.state.count+1
    })
  }
  render(){
    return(
      <div>
        <Child1 count={this.state.count}></Child1>
        <Child2 onIncrement={this.onIncrement}></Child2>
      </div>
    )
  }
}
const Child1=(props)=>{
  return <h3>计数器:{props.count}</h3>
}
const Child2=(props)=>{
  return <button onClick = {()=>props.onIncrement()}>+1</button>
}
ReactDOM.render(<Counter  />,document.getElementById('root'))
```

# Context跨组件传递数据
1. 使用React.createContext()创建Provider,Consumer
2. 使用Provide组件作为父节点
3. 设置value属性,表示要传递的数据
4. 调用Consumer组件来接收数据
```html
const { Provider, Consumer } = React.createContext();

class ContextApp extends React.Component {
    render() {
        return (
            <Provider value='pink'>
                <div className="app">
                    <Node />
                </div>
            </Provider>
        )
    }
}

const Node = props => {
    return (
        <div className="node">
            <SubNode />
        </div>
    )
}

const SubNode = props => {
    return (
        <div className="subNode">
            <Child />
        </div>
    )
}

const Child = props => {
    return <div className="child" ><Consumer>{data=><span>我是子节点---{data}</span>}</Consumer></div>
}
```
# props深入
## children表示组件标签的子节点.当组件标签有子节点时,props就会有该属性.
children 属性与普通props一样,值可以是任意值(文本,React元素,组件,甚至是函数)
```html
class ChildProps extends React.Component{
    render(){
        console.log(this.props)
        return (
            <div><h2>组件标签的子节点{this.props.children}</h2></div>
        )
    }
}
ReactDOM.render(<ChildProps>传递到props的child</ChildProps>,document.getElementById('root'))
```

```html
class ChildProps extends React.Component{
    render(){
        console.log(this.props)
        return (
            <div><h2>组件标签的子节点</h2>{this.props.children}</div>
        )
    }
}
const Test = ()=> <button>组件按钮</button>
ReactDOM.render(<ChildProps><Test/><p>传递到props的child</p></ChildProps>,document.getElementById('root'))
```
函数
```html
class ChildProps extends React.Component{
    render(){
        this.props.children()
        console.log(this.props)
        return (
            <div><h2>组件标签的子节点</h2></div>
        )
    }
}
ReactDOM.render(<ChildProps>{()=>console.info("这是一个函数")}</ChildProps>,document.getElementById('root'))
```
## prop 校验
1. 安装prop-types (npm i prop-types 或者 yarn add prop-types)
2. 导入prop-types包
3. 使用组件名.propTypes={}来给组件的props添加校验规则
4. 校验规则通过PropTypes对象指定
```html
import React from "react"
import PropTypes from "prop-types"
class PropCheck extends React.Component{
    render(){
        const arr = this.props.colors
        console.info(arr)
        const lis = arr.map((item,index)=><li key={index}>{item}</li>)
        return (
            <ul>{lis}</ul>
        )
    }
}
PropCheck.protoTypes= {
    colors:PropTypes.array
}
export default PropCheck
ReactDOM.render(<PropCheck colors = {['red','black']} />,document.getElementById('root'))
```
## prop 默认值
```html
class LPropCheck extends React.Component{
    render(){
        // const arr = this.props.colors
        // console.info(arr)
        // const lis = arr.map((item,index)=><li key={index}>{item}</li>)
        // return (
        //     <ul>{lis}</ul>
        // )
        return <div><h2>test</h2>{this.props.colors}</div>
    }
}
LPropCheck.propTypes= {
    colors:PropTypes.array
}
LPropCheck.defaultProps= {
    colors:["red"]
}
```
