refs & useRef()
在组件当中要选取某一元素,并不会使用文档搜索形式去获取,而是特殊属性ref
,不适合滥用,属于能不用就不用的属性
不推荐搜索获取的原因在于获取速度。在虚拟DOM生成时,根据标记情况就能直接获取,而无需去搜寻整个DOM树
字符串形式
早期使用的是字符串标记,非常简单快捷。
在元素上设置唯一字段,根据对应的字段,通过this.refs
对象访问元素
目前已进入淘汰阶段,当前使用时会提供警告
事实上,当前访问的refs属性,也已经画上了删除线
import React from "react"
export default class App extends React.Component {
render() {
return (
<>
<input type="text" ref="input" />
<button
children="查看"
onClick={() => {
//访问到了input
console.log(this.refs.input)
}}
/>
</>
)
}
}
回调形式
替换字符串方案,切换为函数,通过参数形式传递元素的引用
在
TypeScript
当中,因为多出来的成员需要进行类型声明,会安全很多
回看字符串形式,并不方便做这些限制
//App.tsx
import React from "react"
export default class App extends React.Component {
//TSX 需要声明成员
input: HTMLInputElement | null = null
render() {
return (
<>
<input
type="text"
ref={current => this.input = current}
/>
<button
children="查看"
onClick={() => {
//访问到了input
console.log(this.input)
}}
/>
</>
)
}
}
createRef()
渐渐的,回调形式也不是很合适了,在阅读层面,或许还不够明显,于是添加上了显然易见的createRef()
。并非所有人都使用TypeScript
,这样做也能让属性成员清晰一些
通过createRef()
创建成员属性,将其放入对应元素的ref
属性当中进行绑定
访问时需要多做一步,需要加上current
import React, { createRef } from "react"
export default class App extends React.Component {
//创建容器
input = createRef<HTMLInputElement>()
render() {
return (
<>
<input
type="text"
ref={this.input}
/>
<button
children="查看"
onClick={() => {
//访问到了input
console.log(this.input.current)
}}
/>
</>
)
}
}
useRef()
上述的方法在函数组件
当中,除了字符串形式,其他两种在事实上说,都可以用,但并不合适,最好使用hook
系列之一,useRef()
其特性与createRef()
表现一致,也可以当作一个特殊变量容器使用
import React, { useRef } from "react"
export default function App() {
const input = useRef<HTMLInputElement>(null)
return (
<>
<input type="text" ref={input} />
<button
children="查看"
onClick={() => {
console.log(input.current)
}}
/>
</>
)
}
小结
createRef()
首次登场是16.3
版本,而useRef()
是16.8
- 虽然
createRef()
与回调形式也可以在函数组件
当中使用出效果,但专人专用,它们在总体表现上是比不过useRef()
的,而hook
系列对于class组件
则是完全禁止的 - 对于回调形式,目前是推荐写在老版本当中,最合适的方法还是
createRef()
和useRef()
useRef()
还可以当作一个特殊变量使用,用于既要值变量,又不要网页刷新时
Context & useContext()
Context
,存在的目的是为了方面祖先组件与后代组件之间的数据传递。虽说没有它也可以做到传递,层层props
往下走,就是不方便
通过React.createContext()
创建Context
组件对象
const MyContext = React.createContext("")
传入一个参数,定义
Context
的默认值,也决定了数据类型
对于默认值,只是一个占位的存在,值在多数情况下都是需要动态生成与变动
Provider
通过Context
对象的Provider
组件给后代提供值,此组件需要提供value
字段,表示所传递的值
<MyContext.Provider value="数据">
</MyContext.Provider>
后代组件想要使用值,有多种方式。而不是那种,不管你要不要用,我都塞给你,需要自己伸手去拿
contextType
传统class组件
,设置对应的静态属性contextType
,便能在this.context
上拿到提供的数据
class A extends Component {
//将其指向Context对象
static contextType = MyContext
render() {
return (
<div>
{this.context}
</div>
)
}
}
Consumer
函数组件
没有this,上一种方式就不能用了,强行使用也会被否决掉
function A (){
return (
<p>???</p>
)
}
// Function components do not support contextType
A.contextType = MyContext
此时可以使用Content
对象的另一个成员,Consumer
,让函数组件
同样可以获取到值
function A() {
return (
<>
<p>
<MyContext.Consumer>
{(value) => value}
</MyContext.Consumer>
</p>
<p>
<MyContext.Consumer children={value => value} />
</p>
</>
)
}
两种写法表现一致,后者或许更好理解
并不限于
函数组件
使用,同样可以用于class组件
,只是通常不需要
useContext
hook
的出现,简化了获取流程
function A() {
const context = useContext(MyContext)
return (
<p>
{context}
</p>
)
}
不管是哪一种方式,都是去获取,离当前组件最近的
Provider
提供的值。如果没有找到对应的Provider
,则提取Context
的默认值
祖先组件与后代组件之间传递值(完整示例)
import React, { useContext, useState } from "react"
const MyContext = React.createContext("")
export default function App() {
const [value, setValue] = useState("")
return (
<div style={{ padding: "1em" }}>
<input
type="text"
value={value}
onChange={({ target }) => setValue(target.value)}
/>
<MyContext.Provider value={value}>
<A />
</MyContext.Provider>
</div>
)
}
function A() {
return (
<div>
<h3>A组件</h3>
<B />
</div>
)
}
function B() {
const value = useContext(MyContext)
return (
<div>
<h3>B组件</h3>
<p>{value}</p>
</div>
)
}