3.2 useRef
使用useRef来获取某个组件或元素,返回一个可变的 ref 对象。
ref就是一个类似id的属性,用于获取dom元素
本质上,useRef就是一个其.current属性保存着一个可变值“盒子”.
3.21 Refs and the DOM
ref提供了一种方法允许我们访问DOM节点或在render方法中创建的React元素
作用:提供一种不同于props的方式,在典型数据流之外强制修改子组件。
三种用法:
- String类型(已过时)
- 回调函数
- React.createRef()
String类型的ref有什么问题?
- 需要React追踪当前呈现的组件(不能猜测this),使React变慢。
- 并不按照大多数人期待的“渲染回调模式”来运行,因为ref会被放置在 DataGrid上由于以上原因。
渲染回调模式(React Render Callback Pattern):
将this.props.children当做函数来调用。
数据表格(DataGrid)
- 不可组合(composable)。
何时使用
避免使用refs来做任何可以通过声明式实现来完成的事情。
- 管理焦点,文本选择或媒体播放
- 触发强制动画
- 集成第三方DOM库
创建Refs
通过ref属性附加到React元素
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();//创建
}
render() {
return <div ref={this.myRef} />;//放进ref属性中
}
}
访问Refs
被传递给render中元素时,可以在ref的current属性中被访问。
const node =this.myRef.current
注意:不能在函数组件上使用ref属性,因为他们没有实例。
但可以在函数组件内部使用ref属性,只要它指向一个DOM元素或class组件。
function CustomTextInput(props){
//必须声明 textInput ref才可以引用它
const textInput=useRef(null);
function handleClick(){
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
回调Refs
可以更精细地控制何时refs被设置和接触。
不同于createRef()创建的ref属性,通过回调ref会传递一个函数。
这个函数接受react组件实例或HTML DOM元素作为参数,使他们能在其他地方被存储和访问。
例子:使用ref回调函数,在实例的属性中存储对DOM节点的引用。
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// 使用原生 DOM API 使 text 输入框获得焦点
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// 组件挂载后,让文本框自动获得焦点
this.focusTextInput();
}
render() {
// 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
// 实例上(比如 this.textInput)
return (
<div>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
React在组件挂载时,会调用ref回调函数并传入DOM元素,当卸载时调用它并传入null。
在componentDidMount或componentDidUpdate触发前,React会保证refs一定是最新的。
import {useRef} from 'react'
function App5(){
const element = useRef(null)
const handleClick = () => {
console.log(element.current.) // 获取input
console.log(element.current.value) // 获取到input中的值
}
return (
<>
<input type="text" ref={element} />
<button onClick={handleClick}>获取input标签</button>
</>
)
}
export default App5;
3.3 memo
React 中当组件的 props 或 state 变化时,会重新渲染,实际开发会遇到不必要的渲染场景。
父组件更新时,子组件被迫更新,会造成性能损耗,此时需要用memo来缓存子组件,避免被迫更新
import React, {useState} from 'react'
// 子组件
function Sub(){
console.log('子组件被更新了')
return <div>子组件</div>
}
// 父组件
export default function App(){
const [msg, setMsg] = useState("你好世界");
return (
<>
<h2>内容为:{msg}</h2>
<button onClick={()=>setMsg("Hello World")}>修改Msg</button>
<hr />
<Sub />
</>
)
}
用memo包裹住子组件即可
const Sub = memo(() => {
console.log('子组件被更新了')
return <div>子组件</div>
})
注意,此处父组件只是简单调用子组件,并未给子组件传递任何属性
如果父组件的某个属性给子组件通过props传了值呢?
3.4 useCallback与useMemo
The
useCallbackanduseMemoHooks are similar. The main difference is thatuseMemoreturns a memoized value anduseCallbackreturns a memoized function.
一个返回值,一个返回函数。
useCallback示例
const doSth=useCallback(()=>{setNum(num=>num+1)
},[])//useCallback里丢进函数 第二个参数丢一个空数组代表不检测更新
3.41useCallback基础用法
与useState用法基本一致,但最后会返回一个函数,用一个变量保存起来。
返回的函数a会根据b的变化而变化,如果b始终未发生变化,a也不会生成,避免在不必要的情况下更新
情景:通过属性传递给子组件
比如一个父组件中
const a = useCallback(() => {
return function() {
console.log(b)
}
},[b])
console.log(a)
console.log(a())
- 第一种用法:父子组件函数式传参 子组件受到的参数不变(空数组),自然不会更新,从而减少了组件间不必要的更新
3.42 useMemo
与useCallback差不多,只是useMemo需要再套一个函数
const mySetMsg = useCallback(() => setMsg(()=>"Hello World"), [])
//注意useMemo
const mySetMsg = useMemo(()=>{
return () => setMsg("Hello World")
}, [])