# 组件声明

React 中声明组件有两种方式,一是函数式组件,就是把一个函数作为组件的构造器;二是 class 组件,即用 ES6class 语法声明一个类作为组件的构造器。在 16.8 版本以前函数式组件不能使用组件的 staterefs 属性等诸多特性,这使得函数式组件只能用来定义一些简单组件,对于复杂一些的组件只能使用 class 组件。而此版本以后 React 加入了 Hook ,使得函数式组件也能够使用组件的其它特性。官方文档

# 函数式组件

函数式组件中我们只能用 props 属性,定义一个函数式组件只需要创建一个开头为大写字母的函数,然后再将函数最后 return 回一个 jsx 标签即可。代码如下:

function Person(props) {
    return (
    	<div>
            <h2>{props.name}</h2>
        </div>
    )
}
ReactDOM.render(<Person name="Asuhe" />,document.querySelector('#root'))

# class 组件

class 组件要比函数式组件强大的多,它可以完全使用组件三大属性: statepropsrefs 。它还有生命周期函数可供使用,在生命周期函数中我们可以进行许多操作。但创建一个 class 组件需要从 React对象 中继承一个类 React.Component 。代码如下:

class MyComponent extends React.Component {
    //render 函数必须要有
    render(){
        return (
            <div>
                <h2>Asuhe</h2>
            </div>
        )
    }
}
ReactDOM.render(<MyComponent />,document.querySelector('#root'))

当我们需要在 class 组件上挂载事件处理函数时有两种方式,一是在 constructor 中重写该事件处理函数,二是利用箭头函数和 class 语法声明函数。

class Weather extends React.Component {
    /* 第一种方法 在 jsx 中调用的 this 调用 weather 和设置 state
    constructor (props) {
        super (props)
        this.state = { isHot: true, breeze: ' 微风 ' }
        // 关键步骤:将原型上的 changeWeather 挂载载到组件实例对象的 changeWeather 里并更改 this
        this.changeWeather = this.changeWeather.bind (this)
    }
    changeWeather () {
        let {isHot} = this.state
        this.setState ({
            isHot: !isHot
        })
    }
    // 未重写 changeWeather 前,onClick 绑定的 this.changeWeather 相当于
    //let a = new Weather ()
    //let x = a.changeWeather
    //x ()
    */
    // 第二种方法 不在原型上挂载 changeWeather
    state = { isHot: true, breeze: '微风' }
    changeWeather = () => {
        let { isHot } = this.state
        this.setState({
            isHot: !isHot
        })
    }
    render() {
        return (
            <h2 onClick={this.changeWeather}>
                今天天气很{this.state.isHot ? '炎热' : '凉爽'},{this.state.breeze}
            </h2>
        )
    }
}
ReactDOM.render(<Weather />, el)

# 组件的三大属性

组件实例中有很多属性,但比较常用的就三个分别是:state、props、refs。

image-20220614101741901

# state

state 属性是用来存储该组件实例的状态的。当我们使用 this.setState 函数去修改 state 时,页面会因为组件状态改变而同步改变。以上面的 MyComponent 组件为例,我们可以给组件定义一个初始状态:

class MyComponent extends React.Component {
    /* 第一种方式在构造器里初始化状态
    constructor (props){
    	// 使用了构造器就一定要调用 super 否则 React 报错
        super (props)
        this.state = {name:'Asuhe'}
        // 若不给 super 传 props,则在 constructor 中使用 this.props 可能会出错
    }
    */
    // 第二种方式
    state = {name:'Asuhe'}
    //render 函数必须要有
    render(){
        return (
            <div>
                <h2>{this.state.name}</h2>
            </div>
        )
    }
}

# props

props 属性是用来接收外部传给组件的数据的,如果我们直接在标签上写数据, props 会自动接收该数据。

ReactDOM.render(<MyComponent age={18} />,document.querySelector('#root'))

image-20220614103444298

# 限制 props 的数据类型

有时候我们希望限制传入数据的类型,此时我们需要额外加载一个 prop-types.js 包,里面有 PropTyps 对象以供我们使用。示例代码如下:

// 需求: 
// 1.name 属性必须为 string,age 属性必须为 number,sex 属性必须为 string
// 2.sex 属性必须传入
// 3.sex 若未传入则默认值为 male
class Person extends React.Component {
    static propTypes = {
        name: PropTypes.string,
        age: PropTypes.number,
        //sex 为必须
        sex: PropTypes.string.isRequired
    }
    // 设置默认 props 值
    static defaultProps = {
        sex: 'male'
    }
    render() {
        return (
            <ul>
                <li>name:{this.props.name}</li>
                <li>age:{this.props.age}</li>
                <li>sex:{this.props.sex}</li>
            </ul>
        )
    }
}
let data = {
    name: 'asuhe',
    age: 22,
    // sex: 'male'
}
ReactDOM.render(<Person {...data} />, el)

# refs

当我们需要获取标签 or 组件实例时,可以使用 ref 标记。然后就可以在函数中利用 this.refs 找到该组件 or 标签。 ref 有三种形式分别是:字符串类型、回调函数类型和 refs 对象类型。

# 字符串类型的 ref

字符串形式的 ref 已经不推荐使用, ref 会被自动收录进组件的 refs 里,如同 props。

class Person extends React.Component {
    showData = (event) => {
        event.preventDefault()
        let { input2: { value } } = this.refs
        console.log(value)
    }
    render() {
        return (
            <form>
                <input ref="input1" type="text" placehoder="username" />
                <button onClick={this.showData}>提交</button>
                <input ref="input2" type="password" placehoder="password" />
            </form>
        )
    }
}
ReactDOM.render(<Person />, el)

image-20220614104753532

# 回调函数类型的 ref

回调函数形式的 ref 会将该标签 DOM 传给回调函数的形参,用 this 接住挂载在组件实例上。

class Person extends React.Component {
    showData = (event) => {
        event.preventDefault()
        let { input2: { value } } = this
        console.log(value)
    }
    render() {
        return (
            <form>
                <input ref={a => this.input1 = a} type="text" placehoder="username" />
                <button onClick={this.showData}>提交</button>
                <input ref={b => this.input2 = b} type="password" placehoder="password" />
            </form>
        )
    }
}
ReactDOM.render(<Person />, el)

image-20220614104935057

# refs 对象类型的 ref

使用自定义 ref 对象 ref 对象内含 {current: 被 ref 标记的标签实例} 当多个标签使用同一个 ref 对象标记时,仅保留最后一个。

class Person extends React.Component {
    // 若要 ref 标记多个标签实例,则需要声明多个属性创建多个 React.createRef
    myRefs = React.createRef()
    showData = (event) => {
        event.preventDefault()
        let { current: { value } } = this.myRefs
        console.log(value)
    }
    render() {
        return (
            <form>
                <input ref={this.myRefs} type="text" placehoder="username" />
                <button onClick={this.showData}>提交</button>
                <input ref={this.myRefs} type="password" placehoder="password" />
            </form>
        )
    }
}
ReactDOM.render(<Person />, el)

image-20220614105049968