React快速入门,一文弄懂react的基本使用和高级特性(一)

简介: 在本文中,融合大量案例🌰和动图🕹️进行展示。可以把它当成是 react 的入门宝库,有不懂的语法知识点时或许在这里可以寻找到你的答案并且通过例子运用起来。叮,废话不多说,下面来开始探索 react 的奥秘吧👏

1.png⏰序言


对于刚学习 react 的小伙伴来说,总是从基础开始学习,周一自然也不例外捏。那在下面的文章中,将讲解 react 的基本使用和高级特性,更有周边插件 ReduxReact-router 待你来探寻。

在本文中,融合大量案例🌰和动图🕹️进行展示。可以把它当成是 react 的入门宝库,有不懂的语法知识点时或许在这里可以寻找到你的答案并且通过例子运用起来。

叮,废话不多说,下面来开始探索 react 的奥秘吧👏


📝一、React的基本使用


1、JSX基本使用


(1)变量、表达式

react 中,最基础的内容便是变量和表达式,具体形式如下:

第一种类型:获取变量、插值

import React from 'react'
class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '掘金:星期一研究室',
            imgUrl: 'https://p3-passport.byteacctimg.com/img/user-avatar/cc88f43a329099d65898aff670ea1171~300x300.image',
            flag: true
        }
    }
    render() {
        // 获取变量 插值
        const pElem = <p>{this.state.name}</p>
        return pElem
    }
}
export default JSXBaseDemo
复制代码

注意, react 中插值的形式是单个花括号 {} 的形式。最终浏览器显示的效果如下:

2.png

第二种类型:表达式

import React from 'react'
class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '掘金:星期一研究室',
            imgUrl: 'https://p3-passport.byteacctimg.com/img/user-avatar/cc88f43a329099d65898aff670ea1171~300x300.image',
            flag: true
        }
    }
    render() {
        // 表达式
        const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p>
        return exprElem
    }
}
export default JSXBaseDemo
复制代码

react 中也支持直接在插值里面使用表达式,如上述代码中的 this.state.flag ? 'yes' : 'no' 。最终浏览器的显示效果如下:

3.png


(2)class和style

通常情况下,如果我们要给某一个标签设置类名,那么会给该标签加上一个 class 。而在 react 中,如果想要给一个标签加上一个类,那么需要给其加上 className具体代码如下:

import React from 'react'
import './style.css'
import List from '../List'
class JSXBaseDemo extends React.Component {
    render() {
        // class
        const classElem = <p className="title">设置 css class</p>
        // style
        const styleData = { fontSize: '30px',  color: 'blue' }
        const styleElem1 = <p style={styleData}>设置 style</p>
        // 内联写法,注意 {{ 和 }}
        const styleElem2 = <p style={{ fontSize: '30px',  color: 'blue' }}>设置 style</p>
    // 返回结果
    return [ classElem, styleElem1, styleElem2 ]
    }
}
export default JSXBaseDemo
复制代码

此时浏览器的显示效果为:

4.png

同时需要注意的是,在 react 中,如果要在标签里面写内联样式,那么需要使用双花括号{{}} 来表示。


(3)子元素和组件

第一种类型:子元素

对于子元素来说,它可以在标签里面进行使用。如下代码所示:

import React from 'react'
class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '掘金:星期一研究室',
            imgUrl: 'https://p3-passport.byteacctimg.com/img/user-avatar/cc88f43a329099d65898aff670ea1171~300x300.image',
            flag: true
        }
    }
    render() {
        // 子元素
        const imgElem = <div>
            <p>我的头像</p>
            <img src="xxxx.png"/>
            <img src={this.state.imgUrl}/>
        </div>
        return imgElem
    }
}
export default JSXBaseDemo
复制代码

最终,浏览器的显示效果为:

5.png

第二种类型:加载组件

如果要在 React 中加载一个组件,那么我们可以这么处理。具体代码如下:

import React from 'react'
import './style.css'
import List from '../List'
class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '掘金:星期一研究室',
            imgUrl: 'https://p3-passport.byteacctimg.com/img/user-avatar/cc88f43a329099d65898aff670ea1171~300x300.image',
            flag: true
        }
    }
    render() {
        // 加载组件
        const componentElem = <div>
            <p>JSX 中加载一个组件</p>
            <hr/>
            <List/>
        </div>
        return componentElem
    }
}
export default JSXBaseDemo
复制代码

List.js 组件的代码如下:

import React from 'react'
class List extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'React',
            list: ['a', 'b', 'c']
        }
    }
    render() {
        return <div>
            <p onClick={this.changeName.bind(this)}>{this.state.name}</p>
            <ul>{
                this.state.list.map((item, index) => {
                    return <li key={index}>{item}</li>
                })
            }</ul>
            <button onClick={this.addItem.bind(this)}>添加一项</button>
        </div>
    }
    changeName() {
        this.setState({
            name: '星期一研究室'
        })
    }
    addItem() {
        this.setState({
            list: this.state.list.concat(`${Date.now()}`) // 使用不可变值
        })
    }
}
export default List
复制代码

此时浏览器的显示效果如下:

1.png

由此,我们就将一个组件注入到组件当中。


(4)原生 html

继续,我们来看原生 htmlreact 中是如何使用的。先看以下代码:

import React from 'react'
class JSXBaseDemo extends React.Component {
    render() {
        // 原生 html
        const rawHtml = '<span>富文本内容<i>斜体</i><b>加粗</b></span>'
        const rawHtmlData = {
            // 把 rawHtml 赋值给 __html
            __html: rawHtml // 注意,必须是这种格式
        }
        const rawHtmlElem = <div>
            <p dangerouslySetInnerHTML={rawHtmlData}></p>
            <p>{rawHtml}</p>
        </div>
        return rawHtmlElem
    }
}
export default JSXBaseDemo
复制代码

此时浏览器的显示效果如下:

6.png

大家可以看到,如果要在 react 中使用原生 html ,那么必须使用 const rawHtmlData = { __html: rawHtml } 这种形式,才能将原生 html 代码给解析出来。否则的话, react 是无法正常将原生 html 解析出来的。


2、条件判断


(1)if else

先看下面这段代码:

import React from 'react'
import './style.css'
class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'
        }
    }
    render() {
        const blackBtn = <button className="btn-black">black btn</button>
        const whiteBtn = <button className="btn-white">white btn</button>
        // if else
        if (this.state.theme === 'black') {
            return blackBtn
        } else {
            return whiteBtn
        }
    }
}
export default ConditionDemo
复制代码

style.css 的代码如下:

.title {
    font-size: 30px;
    color: red;
}
.btn-white {
    color: #333;
}
.btn-black {
    background-color: #666;
    color: #fff;;
}
复制代码

此时浏览器的显示效果为:

7.png

大家可以看到,当我们 theme 设置为 black 时,最终显示的效果就是黑色。如果我们把 theme 设置为其他状态,那么最终显示的效果就是白色


(2)三元表达式

先来看一段代码:

import React from 'react'
import './style.css'
class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'
        }
    }
    render() {
        const blackBtn = <button className="btn-black">black btn</button>
        const whiteBtn = <button className="btn-white">white btn</button>
        // 三元运算符
        return <div>
             { this.state.theme === 'black' ? blackBtn : whiteBtn }
        </div>
    }
}
export default ConditionDemo
复制代码

此时浏览器的显示效果为:

8.png

大家可以看到,我们也可以通过三元表达式 this.state.theme === 'black' ? blackBtn : whiteBtn 的方式来对一些条件进行判断。


(3)逻辑运算符 && ||

先来看一段代码:

import React from 'react'
import './style.css'
class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'
        }
    }
    render() {
        const blackBtn = <button className="btn-black">black btn</button>
        const whiteBtn = <button className="btn-white">white btn</button>
        // &&
        return <div>
            { this.state.theme === 'black' && blackBtn }
        </div>
    }
}
export default ConditionDemo
复制代码

此时浏览器的显示结果也是和上述一样的。具体如下:

9.png

this.state.theme === 'black' && blackBtn 这句话的意思为,如果 this.state.theme 的值为 black 时,那么返回 backBtn 的结果。


3、渲染列表


(1)map 和 key

先来看一段代码:

import React from 'react'
class ListDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ]
        }
    }
    render() {
        return <ul>
            { /* 类似于vue中的v-for */
                this.state.list.map(
                    (item, index) => {
                        // 这里的 key 和 Vue 的 key 类似,必填,不能是 index 或 random
                        return <li key={item.id}>
                            index {index}; id {item.id}; title {item.title}
                        </li>
                    }
                )
            }
        </ul>
    }
}
export default ListDemo
复制代码

此时浏览器的显示效果如下:

10.png

react 中,使用的是 list.map 来渲染列表。其中, 需要注意的是, list.map() 是一个函数,那现在我们假设这个函数里面要遵循一套规则 list.map( item => item.id )

这个时候,我们视 .map 为一个数组的重组,那么重组的规则就是 item => item.id 。同时, list.map 返回的是一个数组


4、React的事件


(1)bind this

先来看一段代码:

import React from 'react'
class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan'
        }
        // 修改方法的 this 指向
        // 使用这个写法,组件初始化时,只执行一次bind
        this.clickHandler1 = this.clickHandler1.bind(this)
    }
    render() {
        // this - 使用 bind
        return <p onClick={this.clickHandler1}>
            {this.state.name}
        </p>
    }
    clickHandler1() {
        // console.log('this....', this) // this 默认是 undefined
        this.setState({
            name: 'lisi'
        })
    }
}
export default EventDemo
复制代码

最终浏览器显示的效果为:

11.png

在这段代码中,我们通过 this.clickHandler1 = this.clickHandler1.bind(this) 来对 clickHandler1 进行绑定。


还有另外一种关于 this 的绑定方法。先来看一段代码:

import React from 'react'
class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan'
        }
    }
    render() {
       // this - 使用静态方法
        return <p onClick={this.clickHandler2}>
            clickHandler2 {this.state.name}
        </p>
    }
    // 静态方法,this 指向当前实例
    clickHandler2 = () => {
        this.setState({
            name: 'lisi'
        })
    }
}
export default EventDemo
复制代码

此时浏览器的显示效果为:

12.png

对于上面的这种方式来说, clickHandler2 是一个静态方法,此时它的 this 会指向当前的实例。


(2)关于 event 参数

先来看一段代码:

import React from 'react'
class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan'
        }
    }
    render() {
       // event
        return <a href="https://imooc.com/" onClick={this.clickHandler3}>
            click me
        </a>
    }
    // 获取 event (重点)
    clickHandler3 = (event) => {
        event.preventDefault() // 阻止默认行为
        event.stopPropagation() // 阻止冒泡
        console.log('target', event.target) // 指向当前元素,即当前元素触发
        console.log('current target', event.currentTarget) // 指向当前元素,假象!!!
        // 注意,event 其实是 React 封装的。可以把 __proto__.constructor 看成是 SyntheticEvent 组合事件
        console.log('event', event) // 不是原生的 Event ,原生的是 MouseEvent
        console.log('event.__proto__.constructor', event.__proto__.constructor)
        // 原生 event 如下。其 __proto__.constructor 是 MouseEvent
        console.log('nativeEvent', event.nativeEvent)
        console.log('nativeEvent target', event.nativeEvent.target)  // 指向当前元素,即当前元素触发
        console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!!
    }
}
export default EventDemo
复制代码

此时浏览器的显示效果如下:

1.png

依据以上内容,需要注意的点是:

  • event 是合成事件 SyntheticEvent ,它能够模拟出来 DOM 事件所有的能力;
  • eventReact 封装出来的,而 event.nativeEvent 是原生事件对象;
  • 所有的事件,都会被挂载到 document 上;
  • React 中的事件,和 DOM 事件不一样,和 Vue 事件也不一样。


(3)传递自定义参数

先来看一段代码:

import React from 'react'
class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan',
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ]
        }
    }
    render() {
       // 传递参数 - 用 bind(this, a, b)
        return <ul>{this.state.list.map((item, index) => {
            return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}>
                index {index}; title {item.title}
            </li>
        })}</ul>
    }
    // 传递参数
    clickHandler4(id, title, event) {
        console.log(id, title)
        console.log('event', event) // 最后追加一个参数,即可接收 event
    }
}
export default EventDemo
复制代码

此时,浏览器的显示效果为:

13.png

大家可以看到,我们通过使用 this.clickHandler4.bind(this, item.id, item.title) 这种形式来对 react 中的事件进行参数传递。


(4)注意点

  • React 16 将事件绑定到 document 上;
  • React 17 将事件绑定到 root 组件上;
  • 这样做的好处在于:有利于多个 React 版本并存,例如微前端

如下图所示:

14.png


5、表单


(1)受控组件

先来看一段代码:

import React from 'react'
class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '星期一研究室',
            info: '个人信息',
            city: 'GuangDong',
            flag: true,
            gender: 'female'
        }
    }
    render() {
        // 受控组件
        return <div>
            <p>{this.state.name}</p>
            <label htmlFor="inputName">姓名:</label> {/* 用 htmlFor 代替 for */}
            <input id="inputName" value={this.state.name} onChange={this.onInputChange}/>
        </div>
    }
    onInputChange = (e) => {
        this.setState({
            name: e.target.value
        })
    }
}
export default FormDemo
复制代码

此时浏览器的显示效果如下:

1.png

react 中,通过使用 onChange 事件来手动修改 state 里面的值。


(2)input textarea select 用value

上面我们已经讲解了 input ,接下来我们来看 textareaselect

先来看 textarea 相关的代码:

import React from 'react'
class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '星期一研究室',
            info: '个人信息',
            city: 'GuangDong',
            flag: true,
            gender: 'female'
        }
    }
    render() {
        // textarea - 使用 value
        return <div>
            <textarea value={this.state.info} onChange={this.onTextareaChange}/>
            <p>{this.state.info}</p>
        </div>
    }
    onTextareaChange = (e) => {
        this.setState({
            info: e.target.value
        })
    }
}
export default FormDemo
复制代码

此时浏览器的打印效果是:

2.png

同样地, textarea 也是用 valueonChange 来对进行绑定。


继续来看 select具体代码如下:

import React from 'react'
class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '星期一研究室',
            info: '个人信息',
            city: 'GuangDong',
            flag: true,
            gender: 'female'
        }
    }
    render() {
        // select - 使用 value
        return <div>
            <select value={this.state.city} onChange={this.onSelectChange}>
                <option value="beijing">北京</option>
                <option value="shanghai">上海</option>
                <option value="shenzhen">深圳</option>
            </select>
            <p>{this.state.city}</p>
        </div>
    }
    onSelectChange = (e) => {
        this.setState({
            city: e.target.value
        })
    }
}
export default FormDemo
复制代码

此时,浏览器的显示效果为:

3.png

inputtextarea 一样,也是通过操作 valueonChange ,来改变最终的值。


(3)checkbox radio 用 checked

先来看 ckeckbox代码如下:

import React from 'react'
class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '星期一研究室',
            info: '个人信息',
            city: 'GuangDong',
            flag: true,
            gender: 'female'
        }
    }
    render() {
        // checkbox
        return <div>
            <input type="checkbox" checked={this.state.flag} onChange={this.onCheckboxChange}/>
            <p>{this.state.flag.toString()}</p>
        </div>
    }
    onCheckboxChange = () => {
        this.setState({
            flag: !this.state.flag
        })
    }
}
export default FormDemo
复制代码

此时浏览器的显示效果为:

4.png

在上面的代码中, checkbox 通过操作 checkedonChange ,来改变 state 的值。


radio 也是类似,如下代码所示:

import React from 'react'
class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '星期一研究室',
            info: '个人信息',
            city: 'GuangDong',
            flag: true,
            gender: 'female'
        }
    }
    render() {
        // radio
        return <div>
            male <input type="radio" name="gender" value="male" checked={this.state.gender === 'male'} onChange={this.onRadioChange}/>
            female <input type="radio" name="gender" value="female" checked={this.state.gender === 'female'} onChange={this.onRadioChange}/>
            <p>{this.state.gender}</p>
        </div>
    }
    onRadioChange = (e) => {
        this.setState({
            gender: e.target.value
        })
    }
}
export default FormDemo
复制代码

此时浏览器的显示效果是:

5.png


6、组件使用

对于父子组件的使用来说,我们需要明白三个知识点:props 传递数据、props 传递函数和 props 类型检查。

先来看一段代码:

import React from 'react'
import PropTypes from 'prop-types'
class Input extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            title: ''
        }
    }
    render() {
        return <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    onTitleChange = (e) => {
        this.setState({
            title: e.target.value
        })
    }
    onSubmit = () => {
        const { submitTitle } = this.props
        submitTitle(this.state.title)
        this.setState({
            title: ''
        })
    }
}
// props 类型检查
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired
}
class List extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props
        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }
}
// props 类型检查
// 通过propTypes可以清楚地知道list需要一个什么类型的数据
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}
class Footer extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <p>
            {this.props.text}
            {this.props.length}
        </p>
    }
    componentDidUpdate() {
        console.log('footer did update')
    }
    shouldComponentUpdate(nextProps, nextState) {
        if (nextProps.text !== this.props.text
            || nextProps.length !== this.props.length) {
            return true // 可以渲染
        }
        return false // 不重复渲染
    }
    // React 默认:父组件有更新,子组件则无条件也更新!!!
    // 性能优化对于 React 更加重要!
    // SCU 一定要每次都用吗?—— 需要的时候才优化(SCU即shouldComponentUpdate)
}
// 父组件
class TodoListDemo extends React.Component {
    constructor(props) {
        super(props)
        // 状态(数据)提升
        // list的数据需要放在父组件
        this.state = {
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ],
            footerInfo: '底部文字'
        }
    }
    render() {
        return <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
            <Footer text={this.state.footerInfo} length={this.state.list.length}/>
        </div>
    }
    onSubmitTitle = (title) => {
        this.setState({
            list: this.state.list.concat({
                id: `id-${Date.now()}`,
                title
            })
        })
    }
}
export default TodoListDemo
复制代码

此时浏览器的显示效果如下:

6.png

依据以上代码,我们来对 props 的各个类型进行介绍。


(1)props 传递数据

最后一个 TodoListDemo 是父组件,其他都是子组件。在 Input 组件和 List 组件中,我们将 props 属性的内容,以 this.props.xxx 的方式,传递给父组件


(2)props 传递函数

React 在传递函数这一部分和 vue 是不一样的。对于 vue 来说,如果有一个父组件要传递函数给子组件,子组件如果想要触发这个函数,那么需要使用事件传递和 $emit 的方式来解决。

大家定位到 Input 组件中,在这里,我们将 submitTitle 以函数的形式,传递给父组件中的 onSubmitTitle


(3)props 类型检查

大家定位到两处 props 类型检查的地方。使用 react 中的 PropTypes ,我们可以对当前所使用的属性进行一个类型检查。比如说: submitTitle: PropTypes.func.isRequired 表明的是, submitTitle 是一个函数,并且是一个必填项。

就这样,通过上面的例子,我们学习了属性传递属性验证以及父组件和子组件之间怎么通过传事件的形式来进行通信


7、setState


(1)不可变值

所谓不可变值,即所设置的值永不改变。那这个时候,我们就需要去创建一个副本,来设置 state 的值。

来看几个要点:

第一点:state  要在构造函数中定义。如下代码所示:

import React from 'react'
// 函数组件,默认没有 state
class StateDemo extends React.Component {
    constructor(props) {
        super(props)
        // 第一,state 要在构造函数中定义
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
        </div>
    }
}
export default StateDemo
复制代码

第二点,不要直接修改 state ,要使用不可变值。如下代码所示:

import React from 'react'
// 函数组件,默认没有 state
class StateDemo extends React.Component {
    constructor(props) {
        super(props)
        // 第一,state 要在构造函数中定义
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
          <button onClick={this.increase}>累加</button>
        </div>
    }
    increase= () => {
        // 第二,不要直接修改 state,使用不可变值
        // this.state.count++ // 错误写法,会直接修改原来的值
        this.setState({
            count: this.state.count + 1 // ShouldComponentUpdate → SCU
        })
    }
}
export default StateDemo
复制代码

大家可以看到,在上面的代码中,我们通过 this.state({}) 这种形式,来修改 state 的值。值得注意的是,很多小伙伴会直接使用 this.state.count++ 来修改 state 的值,这在 react 中是非常不允许的。因此,要注意这个要点。

第三点,在 react 中操作数组的值。如下代码所示:

// 不可变值(函数式编程,纯函数) - 数组
const list5Copy = this.state.list5.slice()
list5Copy.splice(2, 0, 'a') // 中间插入/删除
this.setState({
    list1: this.state.list1.concat(100), // 追加
    list2: [...this.state.list2, 100], // 追加
    list3: this.state.list3.slice(0, 3), // 截取
    list4: this.state.list4.filter(item => item > 100), // 筛选
    list5: list5Copy // 其他操作
})
// 注意,不能直接对 this.state.list 进行 push pop splice 等,这样违反不可变值
复制代码

第四点,在 react 中操作对象的值。如下代码所示:

// 不可变值 - 对象
this.setState({
    obj1: Object.assign({}, this.state.obj1, {a: 100}),
    obj2: {...this.state.obj2, a: 100}
})
// 注意,不能直接对 this.state.obj 进行属性设置,即 this.state.obj.xxx 这样的形式,这种形式会违反不可变值
复制代码


(2)可能是异步更新

react 中的 state ,有可能是异步更新。来看一段代码:

import React from 'react'
class StateDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
          <button onClick={this.increase}>累加</button>
        </div>
    }
    increase= () => {
        // setState 可能是异步更新(也有可能是同步更新) 
        this.setState({
            count: this.state.count + 1
        }, () => {
            // 联想 Vue $nextTick - DOM
            console.log('count by callback', this.state.count) // 回调函数中可以拿到最新的 state
        })
        console.log('count', this.state.count) // 异步的,拿不到最新值
    }
}
export default StateDemo
复制代码

此时浏览器的显示效果为:

7.png

大家可以看到,this.state 前半部分并不能同一时间得到更新,所以它是异步操作。而后面的箭头函数中的内容可以得到同步更新,所以后面函数的部分是同步操作


值得注意的是, setTimeoutsetState 中是同步的来看一段代码:

import React from 'react'
class StateDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
          <button onClick={this.increase}>累加</button>
        </div>
    }
    increase= () => {
        // setTimeout 中 setState 是同步的
        setTimeout(() => {
            this.setState({
                count: this.state.count + 1
            })
            console.log('count in setTimeout', this.state.count)
        }, 0)
    }
}
export default StateDemo
复制代码

此时,浏览器的显示效果为:

8.png


还有一个要注意的点是,如果是自己定义的 DOM 事件,那么在 setState 中是同步的,用在 componentDidMount 中。

如果是销毁事件,那么用在 componentWillMount 生命周期中。代码如下:

import React from 'react'
class StateDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
          <button onClick={this.increase}>累加</button>
        </div>
    }
    bodyClickHandler = () => {
        this.setState({
            count: this.state.count + 1
        })
        console.log('count in body event', this.state.count)
    }
    componentDidMount() {
        // 自己定义的 DOM 事件,setState 是同步的
        document.body.addEventListener('click', this.bodyClickHandler)
    }
    componentWillUnmount() {
        // 及时销毁自定义 DOM 事件
        document.body.removeEventListener('click', this.bodyClickHandler)
        // clearTimeout
    }
}
export default StateDemo
复制代码

此时浏览器的显示效果为:

9.png


(3)可能会被合并

setState 在传入对象时,更新前会被合并。来看一段代码:

import React from 'react'
class StateDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
          <button onClick={this.increase}>累加</button>
        </div>
    }
    increase= () => {
        // 传入对象,会被合并(类似 Object.assign )。执行结果只一次 +1
        this.setState({
            count: this.state.count + 1
        })
        this.setState({
            count: this.state.count + 1
        })
        this.setState({
            count: this.state.count + 1
        })
    }
}
export default StateDemo
复制代码

此时浏览器的显示效果为:

10.png

有小伙伴可能会觉得,一下子多个三个 setState ,那结果应该是 +3 才是。但其实,如果传入的是对象,那么结果会把三个合并为一个,最终只执行一次


还有另外一种情况,如果传入的是数,那么结果不会被合并。来看一段代码:

import React from 'react'
class StateDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
          <button onClick={this.increase}>累加</button>
        </div>
    }
    increase= () => {
        // 传入函数,不会被合并。执行结果是 +3
        this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })
        this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })
        this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })
    }
}
export default StateDemo
复制代码

此时浏览器的显示效果为:

11.png

大家可以看到,如果传入的是函数,那么结果一下子就执行三次了。


8、组件生命周期

react 的组件生命周期,有单组件声明周期父子组件声明周期。其中,父子组件生命周期Vue 类似。

这里附上一个生命周期相关的网站:projects.wojtekmaj.pl/react-lifec…

下面附上生命周期的图:

15.png


相关文章
|
6月前
|
前端开发 API UED
React 18有哪些新特性值得关注
【4月更文挑战第18天】React 18推出了新渲染引擎React Reconciler,提升性能和可扩展性;优化SSR,加快首屏加载;新事件处理API增强控制与可读性;自动批量处理减少渲染次数;引入过渡和并发模式,精细控制更新优先级;改变根节点挂载方式,提升响应速度。不支持IE,新增API和服务端渲染优化。React 18在性能、体验和开发效率上迈出重要一步。
481 2
|
1月前
|
前端开发 开发者
React 18 的新特性
【10月更文挑战第12天】 React 18 引入了并发渲染、自动批处理、新的 Suspense 特性、新的 Hooks 和其他多项改进。并发渲染使渲染过程可中断和恢复,提高了应用响应性;自动批处理优化了事件处理,减少不必要的重新渲染;新的 Suspense 支持数据获取和更好的错误处理;新增的 `useId` 和 `useTransition` Hooks 提供了更多功能;服务器组件和改进的错误边界处理进一步提升了性能和稳定性。这些新特性为开发者提供了强大的工具,帮助构建更高效、更稳定的应用。
|
2月前
|
存储 移动开发 前端开发
探秘react,一文弄懂react的基本使用和高级特性
该文章全面介绍了React的基本使用方法与高级特性,包括JSX语法、组件化设计、状态管理、生命周期方法、Hooks使用、性能优化策略等内容,并探讨了Redux和React Router在项目中的集成与应用。
探秘react,一文弄懂react的基本使用和高级特性
|
3月前
|
前端开发 JavaScript Android开发
React Native 快速入门简直太棒啦!构建跨平台移动应用的捷径,带你开启高效开发之旅!
【8月更文挑战第31天】React Native凭借其跨平台特性、丰富的生态系统及优异性能,成为移动应用开发的热门选择。它允许使用JavaScript和React语法编写一次代码即可在iOS和Android上运行,显著提升开发效率。此外,基于React框架的组件化开发模式使得代码更加易于维护与复用,加之活跃的社区支持与第三方库资源,加速了应用开发流程。尽管作为跨平台框架,React Native在性能上却不输原生应用,支持原生代码优化以实现高效渲染与功能定制。对于开发者而言,React Native简化了移动应用开发流程,是快速构建高质量应用的理想之选。
84 0
|
4月前
|
存储 前端开发 安全
|
5月前
|
前端开发 JavaScript API
React小记(四)_路由的基本使用
React小记(四)_路由的基本使用
43 1
|
5月前
|
移动开发 前端开发 Java
技术笔记:ReactNative学习笔记(一)————(RN)快速入门
技术笔记:ReactNative学习笔记(一)————(RN)快速入门
68 0
|
6月前
|
前端开发 API 开发者
React这些新特性在开发效率上有哪些改进
【4月更文挑战第18天】React 18 提升开发效率,引入新Root API `createRoot`优化挂载,支持渐进式升级,减少迁移成本。新增性能工具如Profiler API和Concurrent Mode,自动化批处理提高性能,减少重渲染。服务器组件优化加载速度,减轻客户端负担。开发者可更高效构建和优化React应用。
114 6
|
6月前
|
前端开发 JavaScript 开发者
React 是什么?有什么特性?有哪些优势?
React 是什么?有什么特性?有哪些优势?
177 1
|
6月前
|
前端开发 JavaScript
React 16.8 新特性:让你的应用更出色(下)
React 16.8 新特性:让你的应用更出色(下)
React 16.8 新特性:让你的应用更出色(下)
下一篇
无影云桌面