React 导读(二)

前言

在上篇文章React 导读(一)中学习到了写第一个 Web 组件,这篇文章将继续以前的目录,开始新的知识点补充:javascript

  1. [x] React 如何编写 Hello World!
  2. [x] React 中三个最基础、最重要的东西
  3. [x] React 中的 JSX
  4. [x] 你的第一个 Web 组件
  5. [ ]React 中最开始须要关注的生命周期
  6. [ ]React 一个组件集合的简单交互
  7. [ ]React 开始一个项目的一点建议
  8. [ ]React 简单的项目结构组织

5、React 中最开始须要关注的生命周期

其实在学习 React 以前,就应该了解目前前端推荐的是组件化开发的方式,React 是让组件化更加简单的库。那么组件开发必不可少的就是生命周期,说直白一点就是运行组件的过程是 React 来作,运行过程当中须要有一些代码钩子来让咱们去调用,在组件执行的某一个地方去执行咱们本身写的代码。这里先介绍拥有的生命周期钩子,下面的方法 constructorrender 不属于生命周期,我按功能分类了一下,也就是学习的时候不必定要循序渐进,应该以学习以后能真正写一些东西为目标:前端

(1) 与组件挂载相关的方法,包括构造函数java

  • constructor
  • componentWillMount
  • componentDidMount
  • componentWillUnmount

最经常使用的生命周期应该是最后 2 个,constructorcomponentWillMount 目前先理解成能知足的功能大致相同,若是这里解释太复杂不太好。ajax

对于最开始关注的是:this.state 的初始化以及 ajax 在哪里请求。typescript

this.state 在 constructor 进行初始化, ajax 推荐在 componentDidMount 中进行请求。
  • componentDidMount 就是在组件已经挂载到 DOM 中后的钩子,能够理解为 jQuery 中提供的 ready 方法。
  • componentWillUnmount 是在组件即将被卸载前一刻的钩子,通常用于取消 componentDidMount 中订阅的事件等做用,清理一些不要的变量等,避免内存泄漏。

下面经过一个简单的例子说明一下:编程

先有一个 foods.json 文件来模拟请求的后台数据。json

[
    {
        "id": 1,
        "name": "香蕉"
    },
    {
        "id": 2,
        "name": "苹果"
    },
    {
        "id": 3,
        "name": "猕猴桃"
    }
]
// 1. 挂载相关的生命周期
class CycleMount extends React.Component {
    constructor() {
        super();
        this.state = {
            foods: []
        };

        console.log('1. constructor 执行了...');
    }
    componentDidMount() {
        // 这里使用原生的 fetch API 进行 ajax 请求,你也可使用 $.ajax 进行请求,原理是同样的,重点是关注 setState 的地方
        fetch('/mock/foods.json',
            {
                method: 'GET',
                headers: new Headers({
                    'Accept': 'application/json'
                })
            }
        ).then(dataResult => {
            if(dataResult.status === 200) {
                return dataResult.json();
            } else {
                return [];
            }
        }).then(data => {
            // 这里的 data 就是 foods.json 里面的数据
            // 调用 setState 来更新 render 里面调用的 this.state 的值
            this.setState({
                foods: data
            });
        });

        console.log('2. componentDidMount 执行了...');
    }
    render() {
        // foods 是一个数组,map 方法是数组自带的方法,能够查询相关 api
        const foodItems = 
            this.state.foods.map(food => {
                return (<li key={food.id}>{food.name}</li>);
            });

        // 这里是返回的最终组件结构
        return (
            <ul>
                {foodItems}
            </ul>
        );
    }
}

上面有了完整的注释,也能看到基本上项目中可能会将代码写到何处,我也打了两个日志,来识别究竟是谁先执行,结果能够本身运行一下,执行顺序就是我标记的1,2。api

好了,基本的学习了,能够本身动手试试订阅一个事件,而后在卸载的时候取消这个事件。数组

(2) 优化相关app

  • shouldComponentUpdate

这个方法比较重要,可是我这里不会介绍得太过于复杂,太复杂只会让重要的部分不那么突出。这个方法的返回值是一个 boolean 类型,分别代码的意义:

  • true 组件应该更新,执行 render 方法以及相关生命周期;
  • false 组件状态没有更新,不执行 render 等方法,意味着网页界面不会改变。

那么它直观上的做用是可以经过返回值来决定界面是否改变,实际的意义就是当咱们知道当前 oldState = this.state 的值和新的 newState = this.state 值彻底相等的时候(或者是新传入的 props)就不用再浪费性能去从新渲染组件了。

API 上的定义是这样的:

/* nextProps: 新的 props, nextState: 新的 state */
shouldComponentUpdate(nextProps, nextState): boolean

举个例子来直观说明一下:

class ComponentOptimize extends React.Component {
    state = {
        count: 0
    };
    shouldComponentUpdate(nextProps, nextState) {
        // 当 count > 10 的时候就不能再从新渲染组件了
        if(nextState.count > 10) {
            return false;
        }

        return true;
    }
    handleUpdateCount() {
        console.log('我点了一下哦!');
        this.setState(prevState => {
            return {
                count: prevState.count + 1
            };
        });
    }
    render() {
        return (
            <div onClick={this.handleUpdateCount.bind(this)} style={{cursor: 'pointer'}}>
                <h1>{this.state.count}</h1>
            </div>
        );
    }
}

图片描述

你会发现 10 事后界面就没有再更新过了,这样应该很直观了。

(3) Props 相关的生命周期

  • componentWillReceiveProps

这个主要是在组件的 props 传入新的值后被调用,不论是不是传的同样的值或者 shouldComponentUpdate 返回了 false,看下例子吧:

class Cat extends React.Component {
    componentWillReceiveProps(nextProps) {
        console.log('改一次我执行一次!');
    }
    shouldComponentUpdate(nextProps, nextState) {
        // 改的名字同样的时候
        return this.props.name !== nextProps.name;
    }
    render() {
        console.log('猫改了一次名字!');
        return (
            <h1>我有新名字了!{this.props.name}</h1>
        );
    }
}

class App extends React.Component {
    state = {
        catName: '噜噜'
    };
    handleChangeCatName() {
        const catNames = ['噜噜', '小白', '小黄', '小黑', '皮卡丘'];
        const catIndex = this.getSomeOneIndex();

        this.setState({
            catName: catNames[catIndex]
        });
    }
    getSomeOneIndex() {
        return Math.floor(Math.random() * 5 + 0);
    }
    render() {
        return (
            <div>
                {/* 给 Cat 传新的名字 */}
                <Cat name={this.state.catName} />
                <button onClick={this.handleChangeCatName.bind(this)}>点我给猫咪取新名字!</button>
            </div>
        );
    }
}

最后确定每次点击按钮都会输出这句的结果 console.log('改一次我执行一次!');

(4) 更新组件相关

  • componentWillUpdate
  • componentDidUpdate

由于都是讲解 API,因此国际惯例的先看下 API 的定义吧:

// 组件更新前执行
componentWillUpdate(nextProps, nextState)
// 组件更新后执行
componentDidUpdate(prevProps, prevState)

能够从定义中看出,它们都接受了两个参数:props && state,不过看变量前缀可以联想点什么。

暂时想不到什么实际项目的例子,随便假设点内容吧。不过这里须要注意的地方是:

  • 1. 这两个方法里面都不要调用 setState!
  • 2. 第一次初始化组件 render 的时候不会执行
  • 3. shouldComponentUpdate 返回 false 不会执行

第一条的缘由:容易造成一个递归的调用,不做就不会死...因此尽可能不要在这里调~目前尚未碰到须要在这里调的需求。
第二条的缘由:额,说好的更新才调,初始化不调用是符合逻辑的。
第三条的缘由:额,这 2 个钩子是与组件更新相关的,因此也符合逻辑的,组件是否更新就是靠 shouldComponentUpdate 返回值。

在上面 Cat 的例子中加入下面的代码能够看下结果:

componentWillUpdate() {
    console.log('componentWillUpdate 执行了!')
}
componentDidUpdate() {
    console.log('componentDidUpdate 执行了!')
}

(5)组件错误

  • componentDidCatch

就是在组件发生异常的时候可能会被调用的钩子,须要注意的有下面的地方:

  • 只能在父级组件捕获子组件的异常;
  • 若是异常被 try...catch 包裹父级组件的钩子就不会执行了。

看个例子吧:

class Cat extends React.Component {
    componentWillReceiveProps(nextProps) {
        // 这里手动抛一个异常,触发咱们的钩子 componentDidCatch
        throw new Error('miao miao~');
    }
    render() {
        let miao = this.props.name;

        return (
            <div>
                {miao}
            </div>
        );
    }
}

class App extends React.Component {
    state = {
        catName: '噜噜',
        isError: false,
    };
    handleChangeCatName() {
        const catNames = ['噜噜', '小白', '小黄', '小黑', '皮卡丘'];
        const catIndex = this.getSomeOneIndex();

        this.setState({
            catName: catNames[catIndex]
        });
    }
    getSomeOneIndex() {
        return Math.floor(Math.random() * 5 + 0);
    }
    componentDidCatch(error, info) {
        console.log(error, info);
        if(error) {
            // 若是有错误信息,就从新渲染一下组件,多是更好的交互
            this.setState({
                isError: true
            });
        }
    }
    render() {
        return (
            <div>
                <Cat name={this.state.catName} />
                {!this.state.isError ?
                    <button onClick={this.handleChangeCatName.bind(this)}>点我给猫咪取新名字!</button> :
                    <p>不要奴才给我取名字了!</p>
                }
            </div>
        );
    }
}

(6) 渲染相关

  • render

额...这个不想写了,先睡觉吧~应该写了这么多个小例子也差很少了~能够动手试试哦!还不清楚的能够 Google 一下,你就知道。

生命周期很重要,其实学到这里也差很少能够上手写点项目熟练一下了,其余的更可能是思惟和编程方面的东西,周期的篇幅单独来一篇吧~其余主题以后再继续吧!
相关文章
相关标签/搜索