React : 类组件和函数组件

211 阅读4分钟

组件 Component

Element V.S Component

元素与组价

  • const div = React.createElement('div', ...) , 这是一个React元素(d小写)

  • const Div = () => React.createElement('div', ...),这是一个React组件(D大写)

什么是组件?

  • 能跟其他的物体组合起来的物件,就是组件

  • 组件并没有明确的定义,靠感觉理解就行

  • 就目前而言,一个返回React元素的函数就是组件

  • 在Vue里,一个构造选项就可以表示一个组件

React两种组件

一、函数组件

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>
}

使用方法: <Welcome name="frank"/>

二、类组件

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>
  }
}

使用方法: <Welcome name="frank"/>

会被翻译成什么?

  • <div/>会被翻译为React.createElement('div')

  • <Welcome/>翻译为React.createElement(Welcome)

image.png

React.createElement的逻辑

  • 如果传入一个字符串'div', 则会创建一个div

  • 如果传入一个函数,则会调用该函数,获取其返回值

  • 如果传入一个类,则在类前面加上new(这会导致执行constructor),获取一个组件对象,然后调用对象的render方法,获取其返回值

class Welcome extends React.Component{
  constructor(){
    super()
    this.state = {n:0}
  }
  render(){
    return <div>hi</div>
  }
}

代码示例

import React from "react"
import ReactDOM from "react-dom"
import "./styles.css"

function App(){
  return (
    <div className="App">
    爸爸 
    <Son/>
    </div>
  )
}
//类组件
class Son extends React.Component{
  constructor(){
    super()
    this.state = {n:0}
  }  //初始化
  add(){
    this.setState({n: this.state.n + 1})
  }
  render(){
    return (
      <div className="Son">
        儿子 n: {this.state.n}
        <button onClick = {() => this.add()} > +1 </button>
        <Grandson/>
      </div>
    )
  }
}
//函数组件
const Grandson = () =>{
  const [n, setN] = React.useState(0) //析构函数
  // 等价于
  // const array = React.useState(0)
  // const n = array[0]
  // const setN = array[1]
  //意思为声明一个state,state初始值为0,用n代表0,setN对0进行修改,setN之后就会得到一个新的n 
  return (
    <div className="Grandson">
      孙子 n: {n}
      <button onClick = {()=> setN(n + 1)}> +1 </button>
    </div>
    )
}
const rootElement = document.getElementById("boot")
ReactDOM.render(<App />, rootElement)

添加props(外部数据)

import React from "react"
import ReactDOM from "react-dom"

import "./styles.css"

function App() {
  return (
    <div className="App">
      爸爸
      <Son messageForSon="儿子你好"/>
    </div>
  )
}
class Son extends React.Component{
  render(){
    return (
      <div className="Son">
        我是儿子,爸爸对我说 [{this.props.messageForSon}]
        <Grandson messageForGrandson="孙子你好" />
      </div>
    )
  }
}
const Grandson = props =>{
  return (
    <div className="Grandson">
      我是孙子,我爸对我说 [{props.messageForGrandson}]
    </div>
  )
}
const rootElement = document.getElementById("root")
ReactDOM.render(<App />, rootElement)
  • <Son messageForSon="儿子你好"/>如果穿的字符串可以直接用" ",如果用的是变量,用{ }

类组件直接读取属性this.props.xxx

函数组件直接读取参数props.xxx

添加state(内部数据)

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  )
}
class Son extends React.Component{
  constructor (){
    super()
    this.state = {n:0}
  }
  add(){
    //this.setState({n: this.state.n + 1})
    //等价于
    //this.setState((state)=>{
    //  return {n: state.n + 1}
   // })
   //等价于
   this.setState(state=>{
     const n = state.n + 1
     return {n}
     //setState是等会改变n
   })
  }
  render(){
    return(
      <div className="Son">
        儿子 n:{this.state.n}
        <button onClick = {()=>this.add()}>+1</button>
        <Grandson />
      </div>
    )
  }
}
const Grandson = ()=>{
  const [n, setN] = React.useState(0)
  return (
    <div className="Grandson">
      孙子 n: {n}
      <button onClick = {()=>setN(n + 1)}>+1</button>
    </div>
    //setN永远不会去改变n,会产生一个新的n
  )
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
  • 类组件用 this.state读,this.setState写,建议用函数的方式写
this.setState = (state=>{
  const n = state.n + 1
  return {n} //{n:n}
})
  • 函数组件用useState返回数组,第一项读,第二项写

类组件注意事项

this.state.n +=1 无效?

  • 其实n已经改变了,只不过UI不会自动更新而已

  • 调用setState才会触发UI更新(异步更新)

  • 因为React没有像Vue监听data一样监听state

setState会异步更新UI

  • setState之后,state不会马上改变,立马读state会失败

  • 更推荐的方式是setState

this.setState(this.state)不推荐

  • React希望我们不要修改就state(不可变数据)

  • 常用代码:setState({n:state.n+1})

总结: 这是一种函数式理念

函数组件注意事项

跟类组件类似的地方

  • 也要通过setX(新值)来更新UI

跟类组件不同的地方

  • 没有this,一律用参数和变量

两种编程模型

image.png

复杂的state

state里面不止只有n,若还有m

  • 类组件代码
class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0,
      m: 0
    };
  }
  addN() {
    this.setState({ n: this.state.n + 1 });
    // m 会被覆盖为 undefined 吗?
  }
  addM() {
    this.setState({ m: this.state.m + 1 });
    // n 会被覆盖为 undefined 吗?
  }
  render() {
    return (
      <div className="Son">
        儿子 n: {this.state.n}
        <button onClick={() => this.addN()}>n+1</button>
        m: {this.state.m}
        <button onClick={() => this.addM()}>m+1</button>
        <Grandson />
      </div>
    );
  }
}
  • 函数组件代码
const Grandson = () => {
  const [n, setN] = React.useState(0);
  const [m, setM] = React.useState(0);
  return (
    <div className="Grandson">
    孙子 n:{n}
    <button onClick={() => setN(n + 1)}>n+1</button>
    m:{m}
    <button onClick={() => setM(m + 1)}>m+1</button>
    </div>
  );
};
  • 不推荐写法

image.png

如果想上面那样写就:

image.png

总结:

  • 类组件的setState会自动合并第一层属性,但是并不会合并第二层属性

  • 如果想要合并怎么办?使用Object.assign或者...操操作符

  • 函数组件的setX则完全不会帮你合并,要合并自己用...操作符合并

image.png

image.png

事件绑定

类组件的事件绑定

image.png

image.png

image.png

image.png

回答

  • 蓝色和红色的区别

蓝色函数本身的属性,这意味着每个Son组件都有自己的addN,如果有两个Son,就有两个addN

红色函数是对象的公用属性(也就是原型上的属性),这意味着所有Son组件共用一个addN

  • 为什么this会变/不变

所有函数的this都是参数,由调用决定,所以可变

唯独箭头函数的this不变,因为箭头函数不接受this

总结

image.png

image.png