react.js基础精讲(1)中todoList代码中有许多需要优化的地方
使用ES6的解构赋值进行优化
TodoItem.js中第一处代码优化
- 调用 父组件传递过来的数据 时可以进行代码优化,使用ES6的解构赋值:
- 解释:
- 父组件传递到子组件的数据是存放在属性中的,而众属性是放在props中的。
假设this.props={AA:'AA值',BB:'BB值',CC:'CC值',..};
使用ES6的解构赋值,可以一次性赋值多个变量:在此基础上,接下来需要调用属性时直接使用1
const {AA,BB,CC,...}=this.props;
AA
即可。 const { content } = this.props;
相当于const content = this.props.content;
- 在
return()
中属性名content
被{}
包裹是因为**JSX语法中调用JS变量需要使用{}
**。
- 父组件传递到子组件的数据是存放在属性中的,而众属性是放在props中的。
TodoItem.js中第二处代码优化
原本的代码:
1 | handelClick() { |
同样进行优化后:
1 | handelClick() { |
引用顺序的优化
TodoList.js中第一处代码优化
原本的代码:
1 | import React, { Component, Fragment } from 'react';/* 快捷键imrc */ |
一般我们将组件的引用放在前面,样式的引用放在后面,进行优化后:
1 | import React, { Component, Fragment } from 'react';/* 快捷键imrc */ |
在构造函数中绑定this
指向(bind
)
TodoList.js中第二处代码优化
- 原本在
return()
中绑定事件函数的this
指向,但是为了性能优化我们应该在构造函数constructor
中绑定this
指向:
TodoList.js中第三处代码优化
- 将父组件的
handleItemDelete
方法传给子组件前我们需要绑定this
指向,防止传过去以后this
指向子组件以至于找不到该方法而报错。同样的,我们可以将绑定这个动作放到构造函数中完成, 在构造函数中绑定this
指向 :
JSX语法
JSX语法实际上是写一些页面上显示的东西用的,但是在TodoList.js
中出现了一些逻辑代码:
TodoList.js中第四处代码优化
- 我们另外写一个
getTodoItem()
来存放这些逻辑代码,然后在原本需要逻辑代码的位置使用JS表达式调用getTodoItem()
即可(注意JS表达式需要{}
包裹): - 注意:
getTodoItem()
中需要另外写一个return
用于将函数结果返回,否则调用函数以后是没有结果的。
setState()中参数改为函数
- 新版的React中,
setState()
参数 可以是一个 函数 而不是 对象 ,而这个函数最终返回(return
)一个对象。
TodoList.js中第五处代码优化
- 原本的代码:
1
2
3
4
5handleInputChange(e) {
this.setState({
inputValue: e.target.value
})
} - 解释:
- target对应的是事件对象e对应的DOM节点(也就是input节点),input节点的value值实际上就是输入框中的内容,但此时键入后只能在控制台上看到变化,页面输入框中毫无变化。这是因为虽然拿到了内容,但并未改变状态中的值,所以我们还需要修改的是状态中的inputValue,react中改变状态中的数据需要调用组件的setState方法
- 优化后:
1
2
3
4
5
6
7handleInputChange(e) {
this.setState(() => {
return {
inputValue: e.target.value
}
})
} - 结合ES6 return 简写,再次优化:
1
2
3
4
5handleInputChange(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
6handleInputChange(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
9handleBtnClick() {
// 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
9handleBtnClick() {
// prevState相当于this.state,但更靠谱
this.setState((prevState)=>({
/* 通过扩展运算符将原本的list与输入框中得到的inputValue合并数组后赋给list */
list: [...prevState.list, prevState.inputValue],
/* 清空输入框中的值 */
inputValue: ""
}));
}
TodoList.js中第七处代码优化
原本的代码:
1 | handleItemDelete(index) { |
setState()
中参数改为函数,第一次优化后:
1 | handleItemDelete(index) { |
使用prevState
代替this.state
,第二次优化后:
1 | handleItemDelete(index) { |
其中,按照ES6的语法,list:list
可简写为list
(笔记“ES6对象简写”中有详细解释):
1 | handleItemDelete(index) { |
解决key值问题
- 涉及到循环时每一项都应该有自己独立的key值,没有不会报错,但会警告。
key值需要加在被循环的子项最外层上
- 注意:index作为key值时不靠谱的,暂且用着,后期再改。
- key值需要加在被循环的子项最外层上,所以有
<div>
时需要放在<div>
上:1
2
3
4
5
6
7
8
9
10
11
12
13
14getTodoItem() {
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
13getTodoItem() {
return this.state.list.map((item, index) => {
return (
/* 注意return中只能有一个父元素,注释也会被当成子元素 */
<TodoItem
key={index}
content={item}
index={index}
deleteItem={this.handleItemDelete}
/>
)
})
}