TodoList代码优化

react.js基础精讲(1)中todoList代码中有许多需要优化的地方

使用ES6的解构赋值进行优化

TodoItem.js中第一处代码优化

  • 调用 父组件传递过来的数据 时可以进行代码优化,使用ES6的解构赋值解构赋值
  • 解释:
    1. 父组件传递到子组件的数据是存放在属性中的,而众属性是放在props中的
      假设this.props={AA:'AA值',BB:'BB值',CC:'CC值',..};
      使用ES6的解构赋值,可以一次性赋值多个变量:
      1
      const {AA,BB,CC,...}=this.props;
      在此基础上,接下来需要调用属性时直接使用AA即可
    2. const { content } = this.props;相当于const content = this.props.content;
    3. return()中属性名content{}包裹是因为**JSX语法中调用JS变量需要使用{}**。

TodoItem.js中第二处代码优化

原本的代码:

1
2
3
handelClick() {
this.props.deleteItem(this.props.index)
}

同样进行优化后:

1
2
3
4
5
handelClick() {
// index也是父组件传过来的,可以一起解构赋值
const { deleteItem, index } = this.props;
deleteItem(index);
}

引用顺序的优化

TodoList.js中第一处代码优化

原本的代码:

1
2
3
import React, { Component, Fragment } from 'react';/* 快捷键imrc */
import "./style.css"
import TodoItem from "./TodoItem"

一般我们将组件的引用放在前面,样式的引用放在后面,进行优化后:

1
2
3
import React, { Component, Fragment } from 'react';/* 快捷键imrc */
import TodoItem from "./TodoItem"
import "./style.css"

在构造函数中绑定this指向(bind

TodoList.js中第二处代码优化

  • 原本在return()中绑定事件函数的this指向,但是为了性能优化我们应该在构造函数constructor中绑定this指向
    在构造函数constructor中绑定this指向

TodoList.js中第三处代码优化

  • 将父组件的handleItemDelete方法传给子组件前我们需要绑定this指向,防止传过去以后this指向子组件以至于找不到该方法而报错。同样的,我们可以将绑定这个动作放到构造函数中完成, 在构造函数中绑定this指向
    在构造函数constructor中绑定this指向

JSX语法

JSX语法实际上是写一些页面上显示的东西用的,但是在TodoList.js中出现了一些逻辑代码:
JS逻辑代码被{}包裹

TodoList.js中第四处代码优化

  • 我们另外写一个getTodoItem()来存放这些逻辑代码,然后在原本需要逻辑代码的位置使用JS表达式调用getTodoItem()即可(注意JS表达式需要{}包裹):
    优化后
  • 注意getTodoItem()中需要另外写一个return用于将函数结果返回,否则调用函数以后是没有结果的。

setState()中参数改为函数

  • 新版的React中, setState()参数 可以是一个 函数 而不是 对象 ,而这个函数最终返回(return)一个对象

TodoList.js中第五处代码优化

  • 原本的代码
    1
    2
    3
    4
    5
    handleInputChange(e) {
    this.setState({
    inputValue: e.target.value
    })
    }
  • 解释
    • target对应的是事件对象e对应的DOM节点(也就是input节点),input节点的value值实际上就是输入框中的内容,但此时键入后只能在控制台上看到变化,页面输入框中毫无变化。这是因为虽然拿到了内容,但并未改变状态中的值,所以我们还需要修改的是状态中的inputValue,react中改变状态中的数据需要调用组件的setState方法
  • 优化后
    1
    2
    3
    4
    5
    6
    7
    handleInputChange(e) {
    this.setState(() => {
    return {
    inputValue: e.target.value
    }
    })
    }
  • 结合ES6 return 简写再次优化
    1
    2
    3
    4
    5
    handleInputChange(e) {
    this.setState(() => ({
    inputValue: e.target.value
    }));
    }
  • 会发现报错TypeError: Cannot read property 'value' of null
  • 原因:这是因为setState方法如果传一个函数,它实际上是异步的写法,在后面讲“虚拟DOM”时将会讲解,主要是为了提升性能。
  • 解决方案:此时需要e.target.value放在setState()的外部保存一下,再在内部进行调用:
    1
    2
    3
    4
    5
    6
    handleInputChange(e) {
    const value = e.target.value;
    this.setState(() => ({
    inputValue: value
    }));
    }
  • 注意:箭头函数返回对象时若想省略return则需要用()包裹对象。(具体可参考笔记“函数相关知识点补充”中箭头函数部分

使用 prevState 代替 this.state

  • 我们像上面一样优化TodoList.js中的handleBtnClick()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    handleBtnClick() {
    // prevState相当于this.state,但更靠谱
    this.setState(()=>({
    /* 通过扩展运算符将原本的list与输入框中得到的inputValue合并数组后赋给list */
    list: [...this.state.list, this.state.inputValue],
    /* 清空输入框中的值 */
    inputValue: ""
    }));
    }
  • 运行起来没问题,但其实有更加靠谱的优化方法(如下)。

TodoList.js中第六处代码优化

  • setState()中参数改为函数后,该参数内的函数可传入一个参数prevState
  • prevState指的是你修改数据前的那一次是怎么样的,相当于this.state,使用prevState可以避免你不小心改变state的状态
    1
    2
    3
    4
    5
    6
    7
    8
    9
    handleBtnClick() {
    // prevState相当于this.state,但更靠谱
    this.setState((prevState)=>({
    /* 通过扩展运算符将原本的list与输入框中得到的inputValue合并数组后赋给list */
    list: [...prevState.list, prevState.inputValue],
    /* 清空输入框中的值 */
    inputValue: ""
    }));
    }

TodoList.js中第七处代码优化

原本的代码:

1
2
3
4
5
6
7
handleItemDelete(index) {
const list = [...this.state.list]
list.splice(index, 1);
this.setState({
list: list
})
}

setState()中参数改为函数,第一次优化后:

1
2
3
4
5
6
7
handleItemDelete(index) {
const list = [...this.state.list]
list.splice(index, 1);
this.setState(() => {
return { list: list }
})
}

使用prevState代替this.state,第二次优化后:

1
2
3
4
5
6
7
handleItemDelete(index) {
this.setState((prevState) => {
const list = [...prevState.list];
list.splice(index, 1);
return { list: list };
});
}

其中,按照ES6的语法,list:list可简写为list (笔记“ES6对象简写”中有详细解释):

1
2
3
4
5
6
7
handleItemDelete(index) {
this.setState((prevState) => {
const list = [...prevState.list];
list.splice(index, 1);
return { list };
});
}

解决key值问题

  • 涉及到循环时每一项都应该有自己独立的key值,没有不会报错,但会警告。

key值需要加在被循环的子项最外层上

  • 注意:index作为key值时不靠谱的,暂且用着,后期再改。
  • key值需要加在被循环的子项最外层上,所以有<div>时需要放在<div>上:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    getTodoItem() {
    return this.state.list.map((item, index) => {
    return (
    /* 注意return中只能有一个父元素,注释也会被当成子元素 */
    <div key={index}>
    <TodoItem
    content={item}
    index={index}
    deleteItem={this.handleItemDelete}
    />
    </div>
    )
    })
    }
  • 删去div写在 子组件TodoItem 也可以:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    getTodoItem() {
    return this.state.list.map((item, index) => {
    return (
    /* 注意return中只能有一个父元素,注释也会被当成子元素 */
    <TodoItem
    key={index}
    content={item}
    index={index}
    deleteItem={this.handleItemDelete}
    />
    )
    })
    }

,