【react框架】类式组件实例的三大属性state、props、refs

前言

这次来讲讲属于类式复杂组件的三大属性(函数式的后面hooks里会讲)

state

就是vue中的data()。

import React, { Component } from 'react'

export default class App extends Component {
    constructor() {
        super()
        this.state = { // 初始化状态属性
            str: '我的',
            num: 1
        }
    }
    change = () => {
        this.setState({ str: '你的' }) // 状态必须通过setState进行更新,且更新是一种合并,不是替换。
        // this.state.str = '你的' 是错误的写法,虽然值能被修改但是并不会渲染
    }
    render() {
        let { str, num } = this.state // 读取状态
        return (
            <div onClick={this.change}>点击切换 <span>{str}-{num}</span></div> 
        )
    }
}

我们可以看到核心的修改state方法是react对象上的setState()方法,每次且修改都会自动渲染组件,就会重新执行render。

网上说,状态不是固定叫state,而是约定俗成,可以改成别的。但是我自己试了下改成其他的名称就没用了,还是需要写成state。

setState注意事项

这里列举一些注意事项

存储形式为对象

是个key-value的存储形式

this.state = { name: 'xiaoming' }

key的问题

如果setState的属性是一个变量,需要加上[]符号:

this.state = { name: 'xiaoming' }

fn = (key) => { 
	return (event)=>{ 
		this.setState([key]: event.target.value) // 需要以[xxx]的形式去写key值
	}
}

修改是合并不是重新赋值

前面注释里也提到了,例如上面的num变量在setState的时候没有写,但是state里还存在。

可以不写在constructor里面

直接写外面也可以:

export default class App extends Component {
    state = { 
        str: '我的',
        num: 1
    }
    change = () => {
        this.setState({ str: '你的' }) 
    }
    render() {
        let { str, num } = this.state // 读取状态
        return (
            <div onClick={this.change}>点击切换 <span>{str}-{num}</span></div>
        )
    }
}

是个异步操作(重要)

changeState = () => {
    this.setState({ a: '2' }) // 异步操作,进入微任务,先执行下面的
    console.log('state', this.state); // 打印出旧值
}

所以setState提供了第二个参数用于回调

changeState = () => {
    this.setState({ a: '2' }, () => { console.log('state', this.state) })
}

是个语法糖

上面举例的写法叫做对象式写法,其实是函数式写法的语法糖

changeState = () => {
    this.setState((state, props)=>{
        // 这里可以拿到state, props
        return { a: '2' } // 最后必须返回出去
	}, () => { console.log('state', this.state) })
}

两种写法可以看具体的需求,没有固定死的规定。

不要修改原数据

具体可看我这个博客


props

就是vue中的props,直接看如何使用

子组件中:

import React, { Component } from 'react'

export default class Small extends Component {
    render() {
        const { name, age } = this.props; // 这样直接拿到父组件的值,且是只读的,不可修改
        return ( 
            <div>名字{name}-年龄{age}</div>
        )
    }
}

父组件中:

import React, { Component } from 'react'
import Small from "./Small"

export default class App extends Component {
    render() {
        return (
            <div>
                <Small name="xiaoming" age="18"></Small>
            </div>
        )
    }
}

父组件还可以直接展开的形式传入:

export default class App extends Component {
    render() {
        let info = { name: 'xiaoming', age: 18 }
        return (
            <div>
                <Small {...info}></Small>
                <Small {...info} name="222"></Small> // 后者会覆盖前者
            </div>
        )
    }
}

注意:按照jsx规则,为什么不是<Small {{...info}}></Small>,是因为react给我们提供的写法,可以直接...obj去展开一个对象,所以是<Small {...info}></Small>

如何限制

可以给props里的属性进行类型限制和是否必输入,首先你要有对应的库:

npm install prop-types

然后每个组件:

import React, { Component } from 'react'
import PropTypes from 'prop-types' // 记得要引入这个库

export default class Small extends Component {
    render() {
        const { name, age } = this.props;
        return (
            <div>名字{name}-年龄{age}</div>
        )
    }
}
// 对标签属性进行类型、必要性的限制
Small.propTypes = { // 这个属性react会捕捉到,然后下面的PropTypes是个库对象,帮我们处理限制的
    name: PropTypes.string.isRequired, // 限制必传,且为字符串
    age: PropTypes.number,// 限制为数值
    speak: PropTypes.func,// 限制为函数
}
//指定默认标签属性值
Small.defaultProps = {
    age: 18
}

可以看到其实就是给类本身加上两个属性propTypesdefaultProps 对其进行限制,那么其实可以用es6语法的static进行简写:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class Small extends Component {
    // 对标签属性进行类型、必要性的限制
    static propTypes = { // 这个属性react会捕捉到,然后下面的PropTypes是个库对象,帮我们处理限制的
        name: PropTypes.string.isRequired, // 限制必传,且为字符串
        age: PropTypes.number,// 限制为数值
        speak: PropTypes.func,// 限制为函数
    }
    //指定默认标签属性值
    static defaultProps = {
        age: 18
    }
    render() {
        const { name, age } = this.props;
        return (
            <div>名字{name}-年龄{age}</div>
        )
    }
}

补充

  1. 其实子要是标签中的属性都可以被带到props中,比如className。
  2. 如果在闭合标签中里写内容,那么props会自动生成一个children属性把内容放进去。

refs

这个属性vue的老熟人了,获取dom实例。

首先看最新的写法是React.createRef(),官方也推荐这种写法:

export default class App extends Component {
    smallRef = React.createRef() // 通过createRef创建容器
    showDom = () => {
        console.log("dom", this.smallRef.current);
    }
    render() {
        return (
            <div>
                <div onClick={this.showDom}>获取</div>
                <Small ref={this.smallRef}></Small>
            </div>
        )
    }
}

接下来了一下,不太推荐的两种形式,用ref自动触发的回调:

export default class App extends Component {
    showDom = () => {
        let { smallRef } = this // 然后在函数中直接解构获取
        console.log("dom", smallRef);
    }
    render() {
        return (
            <div>
                <div onClick={this.showDom}>获取</div>
                <Small ref={dom => this.smallRef = dom}></Small> // 在渲染函数render中,ref自动将dom对象作为箭头函数传入,我们直接用this接收
            </div>
        )
    }
}

这种写法会出现会有这样一种情况,因为dom会随着数据的更新而重新渲染,每一次的新渲染react都会先以null为参数,再传入dom对象,以保证每次渲染后dom对象都是最新的。

如果不想每次渲染都触发,可以以绑定函数的形式写回调:

showDom = (dom)=>{
	this.smallRef = dom;
}
<input ref={ this.smallRef } type="text"/><br/><br/>

后两种形式其实对性能没啥影响,但是还是推荐使用第一种。

如果有能实现代替refs的方式作出同样的效果,那么就尽量不要使用refs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值