相关传送门:
<axios github 地址>: https://github.com/axios/axios
<refs 官方文档>:https://reactjs.org/docs/refs-and-the-dom.html
ref的作用其实就是获取、操作dom元素。
在Vue中,是为DOM、标签、组件绑定一个名字,然后再通过this.$refs.name获取该DOM,再进一步操作。
在React中,是通过绑定回调函数的方式获取并操作DOM。
无论是Vue 还是 React, 都需要注意组件生命周期的问题,很可能存在某个阶段,组件还未被初始化,此时我们获取的DOM可能就是NULL。
下面的例子中就存在这样的问题。当我们setState()之后,重新渲染了界面,我们绑定的回调函数重复执行。并且DOM可能为NULL。
解决方法肯定是有的,只需要额外注意一下。
先使用 phpstudy 快速创建 team.php 作为后端测试数据
<?php
header("Access-Control-Allow-Origin:*");
sleep(1);
echo '{"leader":"shenyi","teammates":[{"name":"lisi","age":19},{"name":"zhangsan","age":20}]}';
?>
main.js 代码
import React from 'react'
import ReactDOM from 'react-dom'
import axios from "axios"
class Team extends React.Component {
constructor (props) {
super(props)
this.loadingbox = []
this.state = {
leader : "",
teammates : []
}
}
showLoading () {
this.loadingbox.forEach((ele) => ele.style.display = 'block')
}
hideLoading () {
this.loadingbox.forEach((ele) => ele.style.display = 'none')
}
componentWillMount () {
this.showLoading()
axios.get("http://localhost:8080/team.php").then((res) => {
this.setState({
leader : res.data.leader,
teammates : res.data.teammates
})
this.hideLoading()
})
}
render () {
return <div>
<h1>团队队员</h1>
<span ref = {(ele) => {this.loadingbox.push(ele)}}> 正在加载... </span>
{
this.state.teammates.map((item, index) => {
return <h2 key = { index }> { item.name } —— { item.age } </h2>
})
}
<h1>项目经理</h1>
<span ref = {(ele) => {this.loadingbox.push(ele) }}> 正在加载... </span>
{ this.state.leader }
</div>
}
}
ReactDOM.render(
<Team />,
document.getElementById('root')
);
上面的代码会发现此错误
调试流程:
1、定位错误在 hideLoading() 方法中出错。
2、ele.style.display = 'none' 报错。原因是对NULL进行了操作。为什么会存在NULL?不是应该两个DOM元素吗?
3、loadingbox 数组中为何有6个成员,并且存在NULL。按照我们的理解应该只有两个成员不是吗?
4、发现是this.setState导致的,如果把this.hideLoading() 放在它之前就不会导致错误。为什么呢?
5、原因是 this.setState 导致的重新渲染 render() ,并且渲染过程分为两次(这可能和组件的生命周期有关),第一次 ref 的 ele 为NULL。第二次才正常的DOM对象。
换句话说,整个页面过程一共渲染 render() 了三次,每次添加都往 lonadingbox 中添加了两个ele。其中第二次添加的两个ele都为NULL,所以数组 lonadingbox一共具有6个成员。
知道 this.setState 重新渲染的特性之后。我们将代码改为以下:
import React from 'react'
import ReactDOM from 'react-dom'
import axios from "axios"
class Team extends React.Component {
constructor(props) {
super(props)
this.loadingbox = []
this.state = {
leader : "",
teammates : []
}
}
componentWillMount() {
axios.get("http://localhost:8080/team.php").then((res) => {
this.setState({
leader : res.data.leader,
teammates : res.data.teammates
})
})
}
render() {
return <div>
<h1> 团队队员 </h1>
<span ref = {(ele) => { if (ele) ele.style.display = this.state.teammates.length === 0 ? 'display' : 'none' }}> 正在加载... </span>
{
this.state.teammates.map((item, index) => {
return <h2 key = { index }> { item.name }—— { item.age } </h2>
})
}
<h1> 项目经理 </h1>
<span ref = {(ele) => { if (ele) ele.style.display = this.state.leader === "" ? 'display' : 'none' }}> 正在加载... </span>
{ this.state.leader }
</div>
}
}
ReactDOM.render(
<Team /> ,
document.getElementById('root')
);