# 函数式组件的细节知识 (类Vue中具名插槽的原理)

# 创建函数式组件

函数式组件: 函数里面返回一个JSX元素

函数式组件

1、调取组件可以是单闭合,也可以是双闭合 (双闭合调用,可以把子元素当做属性中的children传递给组件,类似于Vue中的slot)

2、React. Children提供对应的方法专门用来处理传递进来的子元素的children的

3、底层运作的时候,如果虚拟DOM对象的type是一个函数(或者一个类),首先会把函数执行(把解析出来的props传递给这个函数),函数会把一个新的虚拟DOM对象返回,最后整体渲染

4、传递进来的属性是只读的(只能获取,不能直接修改其里面的值),如果非要修改某一个值,可以把其赋值给一个变量(state: 状态)再去修改变量(状态); 再或者把属性深克隆一分,供调取和修改;

# Clock时钟组件

function Clock(props) {

  let index = props. index
  index = 1000
  // props. index = 100 // 是错误的!! 只读属性

  return <div className={props. className} style={props. style}>

    {new Date().toLocaleString()}
    
    {index}

  </div>
}

// ReactDom. render 将虚拟DOM对象转换为真实DOM对象
ReactDom. render(<>
  <Clock index={1} className="text" style={{ color: 'red' }}>

    // props.children
    <span>前端小屋</span>  

  </Clock>
</>, document. getElementById('root'))

// React. createElement 创建虚拟DOM对象
// console. log(React. createElement(Clock, null))

函数式组件的静态性

函数式组件属于静态组件,调取组件渲染出一个结果,后续除非重新渲染组件,否则第一次渲染的内容不会改变(当然React Hooks可以帮我们解决这个问题)

要想实时渲染 需要让虚拟DOM每次都有更新,才会触发由虚拟DOM转变成真实DOM,导致页面渲染

# 函数式组件的静态性

function Clock(props) {
  let time = new Date(). toLocaleString()
  setInterval(_ => {

    time = new Date().toLocaleString()
    console.log(time)  // 该time会变

  }, 1000)

  return <div>

    {time}  // 该值不变,则一直为初始渲染的结果

  </div>
}

ReactDom. render(<>
  <Clock></Clock>
</>, document. getElementById('root'))

# 创建类组件

类组件

创建一个类继承React. Component/React. PureComponent
1、渲染的时候的发现type是一个类,则会创建当前类的实例

2、同样会把解析出来的props传递给当前这个类,并且REACT会帮我们把props挂载到当前类的实例上(this. props即可操作属性),但是默认情况下是执行完constructor才把props挂载到实例上的,所以在constructor中无法基于this. props获取

3、super()类似于call继承 React. Component. call(this, props),React. component()执行时,会把解析出来的props继承到当前类的实例上,前提是super(props)

4、定义在constructor函数之外且当前类类里面的方法或状态都是挂载在原型上的,直接基于this. xxx调用即可
前面加static,如:static defaultProps = {... } 是给当前设置属性
若 使用ES7写法,省略constructor,直接写state = {... },则是给实例设置属性,直接基于this. xxx调用即可

# 类组件的属性传递

class Clock extends React. Component {
  constructor(props) {

    // super(props) == this.props
    super(props); // 类似于call继承 React.Component.call(this,props)

  }
  render() {

    let time = new Date().toLocaleString() 
    return <div>
        {time}
    </div>

  }
  // ... 
}

ReactDom. render(<>
  <Clock index={1}>

    <span>前端小屋</span>

  </Clock>
</>, document. getElementById('root'))

# 类组件中的数据驱动

受控组件:顾名思义,受状态管控的组件,即基于数据驱动视图的渲染(类组件也是动态组件) 数据即组件状态 受控组件:直接操作DOM的 ref

setState

setState(partialState, callback)
      partialState: 部分状态
      callback: 当状态更新,视图重新渲染完触发的回调函数 (类似于vue中的$nextTick)

class Clock extends React. Component {
  constructor(props) {

    super(props); 
    // 初始化状态写法1 目的:(把后续需要用到的状态初始化)
    this.state = {
      time: new Date().toLocaleString() 
    }

  }
  // 初始化状态写法2 ES7中规定下述写法是直接给实例设置属性
  state = {

    time: new Date().toLocaleString()

  }
  render() {

    // 在渲染的时候使用状态
    let time = new Date().toLocaleString() 
    return <div>
        {this.state.time}
    </div>

  }, 
  // 第一次加载完成的生命周期函数
  componentDidMount() {

    setInterval(_ => {
      // 可以修改状态,但是不能控制视图重新渲染
      // this.state.time = new Date().toLocaleString()
      this.setState({
        time: new Date().toLocaleString()
      })
    }, 1000)

  }
}

ReactDom. render(<>
  <Clock index={1}>

    <span>前端小屋</span>

  </Clock>
</>, document. getElementById('root'))

# 类组件方法绑定的几种形式

组件绑定事件,并传递参数,在组件内绑定箭头函数,保证方法中的this指向当前类的实例

class Clock extends React. Component {
  
  render() {

    ...
    return <div>
        ...

        <button onClick={this.handle}></button>

        <button onClick={ev => {
          this.handle(ev, 100)
        }}></button>

        <button onClick={this.handle.bind(this, 100)}></button>
    </div>

  }, 
  handle = (ev, n) => {

    console.log(ev, n)

  }
}

# 类组件中ref获取当前元素

ref在真实项目中的写法

ref拓展

<button ref={

&nbsp;&nbsp;&nbsp;&nbsp;函数表达式写法 <br>
&nbsp;&nbsp;&nbsp;&nbsp;x => 当前操作的元素 <br>
&nbsp;&nbsp;&nbsp;&nbsp;this.btn = x; 直接把元素挂载到实例,不走refs <br>

}>

class Clock extends React. Component {
  
  render() {

    ...
    return <div>
        ...

        // <button ref="btn">获取元素</button>
        <button ref={
          // 函数表达式写法 
          // x => 当前操作的元素
          this.btn = x; // 直接把元素挂载到实例
        }></button>
    </div>

  }, 
  handle = (ev, n) => {

    console.log(ev, n)

  }, 
  componentDidMount() {

    this.autoTimer = setInterval(_ => {
      this.setState({
        time: new Date().toLocaleString()
      })
    }, 1000)

    // 获取DOM元素
    this.btn.addEventListenter('click', _ => {
      clearInterval(this.autoTimer)
    })

  }
}

# 类组件中属性的设置规则

类组件中,若想给组件设置属性规则,需使用prop-types插件

属性规则设置: prop-types

        1、安装插件
        2、设置默认值 Vote. defaultProps = {}
        3、设置传递的值类型是否必传项等 Vote. propTypes = {}         4、render函数里将属性解构出来

class Vote extends React. Component {
  // 设置默认属性 (不传的情况下)
  static defaultProps = {

    supNum: 0
    oppNum: 0

  }; 
  // 设置传递的值类型 和 是否必传
  static proTypes = {

    title: PropType.string.isRequired,
    supNum: PropType.number,
    oppNum: PropType.number

  }

  render() {

    let { title, supNum, oppNum } = this.props

  }
}

# React中的事件绑定

React中的事件绑定是合成事件绑定(获取的事件对象也是合成事件对象)
合成事件是基于事件委托实现的(把所有的事件绑定给最外层的元素, 不论点击到哪一个都会冒泡到最外层)
目的是:实现PC和移动端的兼容性,例如:移动端的click存在300ms延迟,我们设置的onClick在PC端被识别为click,在移动端被识别为touch事件模型
事件对象也是React内部合成的一套对象,但是对于我们获取和操作没有影响,因为它把每一个属性值都get/set了
ev. persist()变为原生事件对象 🎉 💯