React中ref的使用(尽量不用)
ref->reference 引用
在React中我们可以使用ref来直接完成DOM的引用。
但是尽量不要使用ref来获取页面上的DOM,要时刻记得我们要减少去操作DOM,我们在React中应该直接去操作state数据,十分复杂的情况下可使用(比如动画)。
有时候同时使用ref和setState时数据的获取会有延迟,是因为setState是异步的,他并不是马上处理的,所以会停留在上一次的数据。此时可以 将获取DOM的这段代码放在setState的第二个参数的函数里,那么她就会在页面完全渲染好以后再进行DOM元素的获取。
使用e.target
引用对应的DOM元素
TodoList中:
1 | <input |
我们可以通过e.target
来获取元素对应的DOM,也可以通过ref来获取元素对应的DOM。
使用ref引用对应的DOM元素
- 在react16的语法中,ref等于一个函数,这个函数自动接收一个参数(即对应的DOM元素),参数名可以自定义
ref={(input) => { this.input = input }}
- 我们给获取到的对应DOM元素取名input,将它赋值给this.input,则接下来在组件中可以通过this.input来操作这个DOM元素。
可改写 TodoList,render函数中:
1 | <input |
同时修改TodoList中的handleInputChange:
1 | handleInputChange() { |
效果和上面使用e.target
是一样的,但其实并不推荐使用ref。【原代码改回e.target
再继续下一节课的学习】
同时使用ref和setState时的问题与解决
有时候同时使用ref和setState时数据的获取会有延迟,是因为setState是异步的,他并不是马上处理的,所以会停留在上一次的数据。此时可以 将获取DOM的这段代码放在setState的第二个参数的函数里,那么她就会在页面完全渲染好以后再进行DOM元素的获取。
例子
在TodoList的render中,我们给<ul>
增加一个ref去获取对应的DOM节点(也就是ul元素):
1 | //this.ul指向对应的ul元素,refUl是自定义的 |
然后在点击“提交”按钮的绑定事件函数 handleBtnClick 中增加一个打印语句,打印ul元素下的所有div标签的长度。
1 | handleBtnClick() { |
handleBtnClick函数主要作用就是把输入框中的内容加到列表中*(state的list中)并清空输入框,按理说每输入一次div标签就会增加一个。那么输入1时长度应该是1,再次输入2时长度应该是2,可结果明显是DOM元素的获取有延迟:
这是因为setState是异步的,并不会马上执行,所以获取的数据有所延迟,此时可以 将获取DOM的这段代码放在setState的第二个参数的函数里,那么她就会在页面完全渲染好以后再进行DOM元素的获取:
1 | handleBtnClick() { |
结果:
React 的 生命周期函数
- 生命周期函数指在某一时刻组件会自动调用执行的函数。
- React 的 生命周期函数 非常重要,建议每天回顾记忆。
- React生命周期函数以及关于17.0版本生命周期函数的改变
例子:
- “当props或state发生改变时,组件自动调用render函数进行页面挂载。”那么render函数其实就是生命周期函数。
- “当组件被创建时,会自动调用 constructor函数 (构造函数)。”所以你也可以认为 constructor函数 是生命周期函数,但是 constructor函数 并不是React独有的,他是ES6自带的,所以我们不把 constructor函数 归类为 生命周期函数 ,但实际上没区别。
组件/DOM第一次挂载的流程(mounting)
理解:mount 挂载 -> 将组件放到页面上 -> 渲染页面(第一次)
组件第一次挂载的流程:UNSAFE_componentWillMount函数 -> render函数 ->componentDidMount函数
被React弃用并需要替代的函数们
使用componentWillMount时报出警告:
1 | Warning: componentWillMount has been renamed, and is not recommended for use. |
React 宣布 生命周期方法重命名为:
- componentWillMount → UNSAFE_componentWillMount
- componentWillReceiveProps → UNSAFE_componentWillReceiveProps
- componentWillUpdate → UNSAFE_componentWillUpdate
UNSAFE_componentWillMount函数
该函数只在页面即将被挂载时执行,也就是只在第一次将组件放(渲染)到页面上之前才会执行,后续在输入框内输入任何数据都不涉及组件挂载,也就不会执行该函数了。
例子:
在TodoList.js中添加:
render函数(进行页面挂载)
当props或state发生改变时,组件自动调用render函数进行页面挂载,将页面渲染出来。
componentDidMount函数
在render函数下方添加componentDidMount函数:
1 | componentDidMount() { |
结果
【我们在每个生命周期函数内添加了对应的打印语句来观察结果】
可以看到执行顺序是:UNSAFE_componentWillMount函数 -> render函数 ->componentDidMount函数
清空console框,再次输入数据,此时只有render函数被执行。说明只在组件第一次挂载时才执行UNSAFE_componentWillMount函数 和 componentDidMount函数:
组件更新的流程
更新state数据的流程:
shouldComponentUpdate函数 判断是否需要更新,不需要(false)则不再执行下面的函数,需要则顺序执行UNSAFE_componentWillUpdate函数 -> componentDidUpdate函数 。
更新props数据的流程:
比起更新state数据的流程要在最前方多出一个函数:componentWillReceiveProps函数
当一个组件他自己是顶层组件,没有父组件的情况下,他没有接收到props参数,那么就不会执行 函数,比如TodoList中就没有props参数。
shouldComponentUpdate函数
在组件更新之前会被运行,该函数要求返回一个布尔值。
“我的组件需要被更改吗?”返回的布尔值相当于给他一个回答,当返回false时不论你怎么修改state数据,组件都在执行该函数后明白你不需要更新,那么render函数就不会被执行,页面也就不会产生任何的反馈。
注意:shouldComponentUpdate函数其实应该自带两个参数(nextProps,nextState)表示即将更新的state和props,具体可看笔记“React高级内容(3)”中的(使用shouldComponentUpdate做性能优化)
例子:
【我们在每个生命周期函数内添加了对应的打印语句来观察结果】
一样在TodoList中做演示:
当我们鼠标一点进input框就会执行该函数:
如果将返回值设为false,则不论你怎么修改state数据,组件都在执行该函数后明白你不需要更新,那么render函数就不会被执行,页面也就不会产生任何的反馈:
UNSAFE_componentWillUpdate函数
componentWillUpdate函数被弃用,使用会报警告,应使用UNSAFE_componentWillUpdate
函数替代:
1 | react-dom.development.js:12449 Warning: componentWillUpdate has been renamed, and is not recommended for use. |
- 组件被更新之前,他会自动执行,但是他在shouldComponentUpdate之后执行
- 如果shouldComponentUpdate返回true他才执行
- 如果返回false,这个函数就不会被执行了
在TodoList中添加:
1 | UNSAFE_componentWillUpdate() { |
componentDidUpdate函数
组件更新完成后,他会被执行
在TodoList中添加:
1 | componentDidUpdate() { |
结果
【我们在每个生命周期函数内添加了对应的打印语句来观察结果】
shouldComponentUpdate函数返回值为true时的流程:
shouldComponentUpdate函数返回值为false时的流程:
UNSAFE_componentWillReceiveProps函数
componentWillReceiveProps函数已经被React弃用, 我们需要使用UNSAFE_componentWillReceiveProps函数代替他。
执行条件:一个组件要从父组件接受参数
如果这个组件是第一次存在于父组件中,则该函数不会被执行
如果这个组件之前已经存在于父组件中,该函数就会执行
在TodoList中添加该函数:
1 | UNSAFE_componentWillReceiveProps(){ |
然而并无反应,因为TodoList就是顶层函数,他没有父组件,也就不会接受props参数,自然也不存在props更新。
在TodoItem中添加该函数:
1 | UNSAFE_componentWillReceiveProps(){ |
结果
【我们在每个生命周期函数内添加了对应的打印语句来观察结果】
首先我们要明确,TodoItem这个子组件控制的是输入框下面的ul列表。
当我们输入1并提交,第一次往TodoItem中放入数据时,UNSAFE_componentWillReceiveProps函数并没被执行,这是因为 这个组件必须之前已经存在于父组件中,该函数才会执行:
当我们再次输入2并点击提交时,完美符合执行条件,该函数被执行:
把组件从页面去除的流程(unmounting)
当这个组件即将从页面中剔除时,该生命周期函数会被执行。
componentWillUnmount函数
在TodoItem中添加该函数:
1 | componentWillUnmount() { |
结果
【我们在每个生命周期函数内添加了对应的打印语句来观察结果】
输入并提交1以后删除无序列表中的“你好呀-1”:
依次输入并提交1、2,再删除无序列表中的“你好呀-2”:
结果2的原因:TodoList中放在ul标签中的getTodoItem告诉我们,遍历list列表后数组中的每一项都会返回一个子组件TodoItem,所以该无序列表中的每一项都是一个子组件!