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


相关文章
|
3月前
|
存储 前端开发 JavaScript
第三十章 React的路由基本使用
第三十章 React的路由基本使用
|
2月前
|
前端开发 JavaScript API
React小记(四)_路由的基本使用
React小记(四)_路由的基本使用
19 1
|
3月前
|
前端开发
React快速入门
React快速入门
24 0
|
3月前
|
前端开发 JavaScript
React中react-redux的基本使用
React中react-redux的基本使用
|
3月前
|
前端开发
React createContext 与 useContext 的基本使用
React createContext 与 useContext 的基本使用
133 0
|
10月前
|
存储 前端开发 JavaScript
React的诱惑: React-Redux-三大原则和React-Redux-基本使用、优化、综合运用、其他组件使用
React的诱惑: React-Redux-三大原则和React-Redux-基本使用、优化、综合运用、其他组件使用
27 1
|
前端开发
React createContext 与 useContext 的基本使用
React createContext 与 useContext 的基本使用
55 0
|
资源调度 JavaScript 前端开发
React系列教程(4)React Redux快速入门(下)
React系列教程(4)React Redux快速入门(下)
68 0
|
存储 JavaScript 前端开发
React系列教程(4)React Redux快速入门(上)
React系列教程(4)React Redux快速入门
118 0
|
XML 存储 前端开发
React系列教程(1)ReactJS快速入门
React系列教程(1)ReactJS快速入门
121 0