赞
踩
本章内容
上一节我们了解了 React
中的”虚拟DOM“中的”Diff算法““ ,本节我们来说一说 React
中 ref
的使用
TodoList.js
代码input
框中的数据变化时,要求拿到这个input
框节点 DOM
e.target
来实现import React, { Component, Fragment } from "react"; import TodoItem from "./TodoItem"; class TodoList extends Component{ constructor(props) { super(props) this.deleteData = this.deleteData.bind(this) this.addListData = this.addListData.bind(this) this.changeInputValue = this.changeInputValue.bind(this) this.state = { inputValue: '', list: [] } } render() { console.log('render 函数执行了') return ( <Fragment> <div> <input value={this.state.inputValue} onChange={this.changeInputValue} /> <button onClick={this.addListData}> 提交 </button> </div> <ul> {this.getTodoItem()} </ul> </Fragment> ) } getTodoItem() { return this.state.list.map((item, index) => { return <TodoItem key={index} content={item} index={index} deleteFn={this.deleteData}></TodoItem> }) } deleteData(index) { this.setState((prevState) => { const list = [...prevState.list] list.splice(index, 1) return {list} }) } addListData() { this.setState((prevState) => ({ list: [...prevState.list, prevState.inputValue], inputValue: '' })) } // 1、当input框里的值发生变化后,该函数会执行 changeInputValue(e) { // 2、我们试着在控制台打印出 e.target,观察其内容 console.log(e.target) // 3、拿到输入框的数据 const value = e.target.value // 4、更新inputValue 数据 this.setState(() => ({inputValue: value})) } } export default TodoList
运行代码,打开浏览器的控制台,可以看到,每当输入框的数据变化,就会打印出 input
框的 DOM
节点
从上面的演示可以看出,在 React
中,仍然可以使用 e.target
获取事件对应“元素”的DOM
节点
ref
是 reference
的简写,其含义为“引用”React
中,我们可以使用 ref
来获取 DOM
元素。但是一般情况下,我们尽量不要去使用 ref,但有时一些复杂的业务(如:动画),其不可避免的还是会用到页面上的一些 DOM
标签。ref
来实现上面的需求:“当 input
框中的数据变化时,要求拿到这个input
框节点 DOM
”import React, { Component, Fragment } from "react"; import TodoItem from "./TodoItem"; class TodoList extends Component{ constructor(props) { super(props) this.deleteData = this.deleteData.bind(this) this.addListData = this.addListData.bind(this) this.changeInputValue = this.changeInputValue.bind(this) this.state = { inputValue: '', list: [] } } render() { return ( <Fragment> <div> {/* 1、首先给 input 元素添加一个 ref 属性,它等于一个”箭头函数”。 ”箭头函数“自动收到一个参数,名字可以任意取 2、箭头函数使用一个固定的写法 this.input=input,表示, 我构建了一个“引用”ref,这个“引用”叫做 this.input, 它指向 input框对应的 DOM 节点 3、接着我们使用 ref 方式获得的 DOM 节点来替换 changeInputValue 方法里的 e.target.value 写法 */} <input value={this.state.inputValue} onChange={this.changeInputValue} ref={(input) => this.input = input} /> <button onClick={this.addListData}> 提交 </button> </div> <ul> {this.getTodoItem()} </ul> </Fragment> ) } getTodoItem() { return this.state.list.map((item, index) => { return <TodoItem key={index} content={item} index={index} deleteFn={this.deleteData}></TodoItem> }) } deleteData(index) { this.setState((prevState) => { const list = [...prevState.list] list.splice(index, 1) return {list} }) } addListData() { this.setState((prevState) => ({ list: [...prevState.list, prevState.inputValue], inputValue: '' })) } // 4、去掉入参 e, 使用 ref 方式获得的 DOM 节点来替换 e.target.value 写法 changeInputValue() { console.log(this.input) const value = this.input.value this.setState(() => ({inputValue: value})) } } export default TodoList
ref
用于获取 DOM
节点是可以被正常使用的。但,注意不要滥用,React
是“数据驱动”的框架,非特殊情况,不要主动去操作 DOM
,而且,用 ref
时,一不小心就会有bug
,比如和 setState
合用上面我们提到 ref
和 setState
合用时,有的时候会有bug。现在我们用一个例子来说明
需求是这样的:当输入框输入内容并点击“提交”,要求打印出 list
数据的 DOM
节点内容以及长度(即:打印 ul
元素节点,以及ul
中的div
长度)
要实现上面的需求,思路大概是
1、给 ul 绑定 ref 属性,获取到 DOM 节点
2、在“提交”按钮点击事件 “addListData” 中打印 ul 节点,并使用原生的 querySelectorAll 方法 打印 ul 中 div 的长度
import React, { Component, Fragment } from "react"; import TodoItem from "./TodoItem"; class TodoList extends Component{ constructor(props) { super(props) this.deleteData = this.deleteData.bind(this) this.addListData = this.addListData.bind(this) this.changeInputValue = this.changeInputValue.bind(this) this.state = { inputValue: '', list: [] } } render() { return ( <Fragment> <div> <input value={this.state.inputValue} onChange={this.changeInputValue} ref={(input) => this.input = input} /> <button onClick={this.addListData}> 提交 </button> </div> {/* 1、给 ul 绑定 ref 属性,获取到 DOM 节点 */} <ul ref={(ul) => this.ul = ul }> {this.getTodoItem()} </ul> </Fragment> ) } getTodoItem() { return this.state.list.map((item, index) => { return <TodoItem key={index} content={item} index={index} deleteFn={this.deleteData}></TodoItem> }) } deleteData(index) { this.setState((prevState) => { const list = [...prevState.list] list.splice(index, 1) return {list} }) } // 2、在“提交”按钮点击事件 “addListData” 中打印 ul 节点 // 并使用原生的 querySelectorAll 方法 打印 ul 中 div 的长度 addListData() { console.log(this.ul) // 打印 ul 节点 console.log(this.ul.querySelectorAll("div").length) // 使用原生的 querySelectorAll 方法 打印 ul 中 div 的长度 this.setState((prevState) => ({ list: [...prevState.list, prevState.inputValue], inputValue: '' })) } changeInputValue() { const value = this.input.value this.setState(() => ({inputValue: value})) } } export default TodoList
运行代码,回到页面控制台查看 this.ul.querySelectorAll("div").length
打印出的 div
长度,发现输出的长度跟真实的长度不对等,输出长度比实际长度少1个!!!
为什么会出现这个问题呢?
答:React 中 setState 被设计成异步的,当数据变化时, setState 中的函数体并不会立即执行,它要等一会儿。
而 console.log(this.ul.querySelectorAll("div").length) 从页面效果来看是在 setState 底层运行之前执行了
setState
里面有第二个参数(也是一个函数),我们可以在这个函数里执行 console.log(this.ul.querySelectorAll("div").length)
// 2、在“提交”按钮点击事件 “addListData” 中打印 ul 节点
// 并使用原生的 querySelectorAll 方法 打印 ul 中 div 的长度
addListData() {
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputValue],
inputValue: ''
}), () => {
console.log(this.ul) // 打印 ul 节点
console.log(this.ul.querySelectorAll("div").length) // 使用原生的 querySelectorAll 方法 打印 ul 中 div 的长度
})
}
到此,本章内容结束!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。