React进阶

生命周期钩子

关于生命周期钩子,官方文档给出了详细的说明

React生命周期 - 官方文档

image.png

不同阶段调用的生命周期钩子

挂载阶段
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

constructor()
static getDerivedStateFromProps()
render()
componentDidMount()

更新阶段
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()

卸载阶段
当组件从 DOM 中移除时会调用如下方法:

componentWillUnmount()

生命周期钩子函数介绍

constructor(props)

构造函数,创建实例时候会调用,通常用于绑定函数,初始化state。

static getDerivedStateFromProps(props, state)

静态方法,用于state的某些值只依赖于当前的props的场景,并且在初始挂载及后续更新时都会被调用

render()

渲染函数,返回JSX或者React.element。

componentDidMount()

挂载完成,通常在这个钩子中做初始化操作,请求接口、绑定事件、启动定时器等。

shouldComponentUpdate()

用于优化性能,判断是否需要比较更新。

getSnapshotBeforeUpdate(prevProps, prevState)

它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。

componentDidUpdate(prevProps, prevState, snapshot)

更新完成,做一些有副作用的操作,例如请求接口,弹出提示等等。

prevProps和prevState是更新前的props和state,用于逻辑判断中需要对比更新的改动。

componentWillUnmount()

会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,如解除订阅、清除定时器。

getDerivedStateFromProps和getSnapshotBeforeUpdate示例

前端面试刷题网站灵题库,收集大厂面试真题,相关知识点详细解析。】

对于getDerivedStateFromProps和getSnapshotBeforeUpdate不是很容易理解,给出示例代码:

getDerivedStateFromProps对props传入的digit和Child的state的sum计算求和。

注意getDerivedStateFromProps是静态方法,因此没有this。getDerivedStateFromProps的作用就是根据props和state计算新的state,不需要this。

import React from 'react';

class Child extends React.PureComponent {
  	// state完全依赖于props.digit,因此可以使用getDerivedStateFromProps来进行处理
    static getDerivedStateFromProps(props, state) {
        return {
            sum: state.sum + props.digit
        };
    }

    state = {
        sum: 0
    };

    render() {
        return (
            <div>累计:{this.state.sum}</div>
        );
    }
}

class App extends React.PureComponent {
    state = {
        digit: 0
    };

    onButtonClick = () => {
        this.setState(preState => ({
            digit: preState.digit + 1
        }));
    };

    render() {
        return (
            <div>
                数字:{this.state.digit}
                <button onClick={this.onButtonClick}>+1</button>
                <Child digit={this.state.digit} />
            </div>
        );
    }
}

export default App;

对getSnapshotBeforeUpdate的官方文档的示例代码稍加改造。

App有个按钮元素,点击则给list增加元素,ScrollingList组件渲染list,并且如果列表处于底部时候,增加新元素后会自动滚动到底部。

import React from 'react';
import './App.css';

class ScrollingList extends React.Component {
    constructor(props) {
        super(props);
        this.listRef = React.createRef();
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        // 如果有增加的item,则可能需要滚动
        if (prevProps.list.length < this.props.list.length) {
            const list = this.listRef.current;
            // 如果当前没有在回看,需要滚动
            return list.scrollHeight - list.scrollTop - list.clientHeight === 0;
        }
        return false;
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        // 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,
        // 并且没有在会看,说明我们需要滚动到底部
        //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
        if (snapshot) {
            const list = this.listRef.current;
            list.scrollTop = list.scrollHeight;
        }
    }

    render() {
        return (
            <div ref={this.listRef} style={
  {border: '1px solid', height: 100, overflow: 'auto', marginTop: 50}}>
                {
                    this.props.list.map(text => (
                        <li key={text} style={
  {fontSize: 20}}>{text}</li>
                    ))
                }
            </div>
        );
    }
}

class App extends React.PureComponent {
    state = {
        list: []
    };

    onButtonClick = () => {
        this.setState(preState => ({
            list: [...preState.list, preState.list.length]
        }));
    };

    render() {
        return (
            <div>
                <button onClick={this.onButtonClick}>添加</button>
                <ScrollingList list={this.state.list} />
            </div>
        );
    }
}

export default App;

React16更新的生命周期钩子

移除的

UNSAFE_componentWillMount()

UNSAFE_componentWillReceiveProps(nextProps)

UNSAFE_componentWillUpdate(nextProps, nextState)

新增的

static getDerivedStateFromProps(props, state)
getSnapshotBeforeUpdate(prevProps, prevState)

关于setState

setState是异步的吗
出于性能考虑,React将setState进行异步处理,即调用setState时候不会马上更新,而是将setState的值放入一个队列,然后延时批量处理队列中的state。在生命周期钩子和React合成事件中中调用setState,会将数据添加到一个队列中,合并处理,这时候setState是异步的而在其他的React控制之外的地方调用(比如原生dom事件)则不是异步的。
setState异步特性,导致需要注意两个问题

  1. 设置完state,获取数据的时机

由于异步特性,setState改变state数据后,无法马上获取到state的最新值。
由于setState是异步的,如果想获取改变后的值,应该在setState的第二个参数回调函数中访问state的最新值。

  1. 修改数据依赖现有的数据
    由于setState会异步合并处理,因此如果设置的state值依赖于之前的state值,需要给setState传一个函数,

函数参数是之前的state,返回的值是新的改变的state属性。

import React from 'react';

class App extends React.Component {
    state = {
        digit: 1,
        text: 'a'
    };

    componentDidMount() {
        document.getElementById('btn')
            .addEventListener('click', this.onAddButtonClick);
    }

    onAddButtonClick = () => {
        // bad
        // const add1 = () => {
        //     this.setState({digit: this.state.digit + 1});
        // };
        // add1();
        // add1();
        // add1();

        // good
        const add1 = () => {
            this.setState((state) => {
                return {digit: state.digit + 1};
            });
        };
        add1();
        add1();
        add1();
    };

    onAddButtonClick2 = () => {
        // work
        const add1 = () => {
            this.setState({digit: this.state.digit + 1});
        };
        add1();
        add1();
        add1();
    };

    onChangeButtonClick = () => {
        // bad
        // this.setState({text: 'b'});
        // console.log(this.state.text);

        // good
        this.setState({text: 'b'}, () => {
            console.log(this.state.text);
        });
    };

    render() {
        return (
            <div>
                <div>{this.state.digit}</div>
                <button onClick={this.onAddButtonClick}>+3</button>
                <button id="btn">+3(原生事件)</button>
                <hr />
                <div>{this.state.text}</div>
                <button onClick={this.onChangeButtonClick}>修改文本</button>
            </div>
        );
    }
}

export default App;

React更新界面的主要过程可以简单描述为,调用setState之后,React会更新state,然后调用组件的render得到新的state对应的虚拟dom,然后对比当前的和更新后端虚拟dom,判断是否需要更新dom,以及如何更新dom。

import React from 'react';
import './App.css';

class App extends React.Component {
    state = {
        di
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值