|
@@ -869,11 +869,209 @@ export default TestUseMemo;
|
|
|
|
|
|
### 8.4 useCallback
|
|
|
|
|
|
+> 类似与 useMemo,useCallback也是用来缓存的。但是useCallback 只能缓存一个函数。
|
|
|
|
|
|
+```js
|
|
|
+import { useCallback, useEffect, useState } from 'react';
|
|
|
+
|
|
|
+// props 包含children,存储后代React元素
|
|
|
+function MyButton({ children, onClick }) {
|
|
|
+ useEffect(() => {
|
|
|
+ console.log('my button');
|
|
|
+ });
|
|
|
+ return <button onClick={onClick}>{children}</button>;
|
|
|
+}
|
|
|
+
|
|
|
+function TestUseCallback() {
|
|
|
+ let [msg, setMsg] = useState('');
|
|
|
+
|
|
|
+ function handleClick() {
|
|
|
+ setMsg('heheda');
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <p>{msg}</p>
|
|
|
+ <MyButton onClick={handleClick}>按钮文字</MyButton>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default TestUseCallback;
|
|
|
+```
|
|
|
+
|
|
|
+上面代码,父组件TestUseCallback只要重新渲染,handleClick函数就会重建,那么会导致两次MyButton组件绑定的onClick属性不一致,进而出现多次非必要的重新渲染逻辑。
|
|
|
+
|
|
|
+因此,在组件TestUseCallback多次重新渲染中,MyButton组件是不必一起重新渲染的。那么需要将handleClick函数缓存起来即可。
|
|
|
+
|
|
|
+```jsx
|
|
|
+import { useCallback, useEffect, useState } from 'react';
|
|
|
+
|
|
|
+// props 包含children,存储后代React元素
|
|
|
+function MyButton({ children, onClick }) {
|
|
|
+ useEffect(() => {
|
|
|
+ console.log('my button');
|
|
|
+ });
|
|
|
+ return <button onClick={onClick}>{children}</button>;
|
|
|
+}
|
|
|
+
|
|
|
+function TestUseCallback() {
|
|
|
+ let [msg, setMsg] = useState('');
|
|
|
+
|
|
|
+ // 当然这里也可以使用useMemo来缓存。
|
|
|
+ const handleClick = useCallback(() => {
|
|
|
+ setMsg('heheihei');
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <p>{msg}</p>
|
|
|
+ <MyButton onClick={handleClick}>按钮文字</MyButton>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default TestUseCallback;
|
|
|
+```
|
|
|
|
|
|
## 9. refs & dom
|
|
|
|
|
|
+> refs 是在组件中 连接 HTML元素的一种推荐方式。
|
|
|
+
|
|
|
+`useRef` 是一个 React Hook,它能让你引用一个不需要渲染的值。
|
|
|
+
|
|
|
+```
|
|
|
+const ref = useRef(initialValue)
|
|
|
+```
|
|
|
+
|
|
|
+#### 参数
|
|
|
+
|
|
|
+- `initialValue`:ref 对象的 `current` 属性的初始值。可以是任意类型的值。这个参数会首次渲染后被忽略。
|
|
|
+
|
|
|
+#### 返回值
|
|
|
+
|
|
|
+`useRef` 返回一个只有一个属性的对象:
|
|
|
+
|
|
|
+- `current`:最初,它被设置为你传递的 `initialValue`。之后你可以把它设置为其他值。如果你把 ref 对象作为一个 JSX 节点的 `ref` 属性传递给 React,React 将为它设置 `current` 属性。
|
|
|
+
|
|
|
+在后续的渲染中,`useRef` 将返回同一个对象。
|
|
|
+
|
|
|
+下面案例,是用ref对象保存定时器id:
|
|
|
+
|
|
|
+```jsx
|
|
|
+import { useEffect, useState, useRef } from 'react';
|
|
|
+
|
|
|
+// let timer;
|
|
|
+
|
|
|
+function Clock({ title }) {
|
|
|
+ let [now, updateNow] = useState(new Date());
|
|
|
+
|
|
|
+ let timer = useRef(undefined);
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
+ // 启动定时
|
|
|
+ timer.current = setInterval(() => {
|
|
|
+ updateNow(new Date());
|
|
|
+ }, 1000);
|
|
|
+ // 返回函数,在组件卸载前清除定时器
|
|
|
+ return () => {
|
|
|
+ // console.log(timer);
|
|
|
+ clearInterval(timer.current);
|
|
|
+ };
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ console.log(timer.current);
|
|
|
+ }, [title]);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ {title || '时间为'} : {now.toLocaleString()}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default Clock;
|
|
|
+```
|
|
|
+
|
|
|
+下面案例 是 用ref对象 保存DOM对象
|
|
|
+
|
|
|
+```jsx
|
|
|
+import { useCallback, useRef } from 'react';
|
|
|
+
|
|
|
+function RefsDOM() {
|
|
|
+ const handleUpload = useCallback(() => {
|
|
|
+ console.log('你要上传的文件是:', inputRef.current?.value);
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ let inputRef = useRef(null);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <input type="file" ref={inputRef} />
|
|
|
+ <button onClick={handleUpload}>上传</button>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default RefsDOM;
|
|
|
+```
|
|
|
|
|
|
+`forwardRef` lets your component expose a DOM node to parent component with a [ref.](https://react.docschina.org/learn/manipulating-the-dom-with-refs)
|
|
|
|
|
|
+```jsx
|
|
|
+const SomeComponent = forwardRef(render)
|
|
|
+```
|
|
|
+
|
|
|
+`useImperativeHandle` 是 React 中的一个 Hook,它能让你自定义由 [ref](https://react.docschina.org/learn/manipulating-the-dom-with-refs) 暴露出来的句柄。
|
|
|
+
|
|
|
+```
|
|
|
+useImperativeHandle(ref, createHandle, dependencies?)
|
|
|
+```
|
|
|
+
|
|
|
+```jsx
|
|
|
+import { forwardRef, useImperativeHandle, useRef } from 'react';
|
|
|
+
|
|
|
+function FileInput(props, ref) {
|
|
|
+ let inputRef = useRef();
|
|
|
+ useImperativeHandle(
|
|
|
+ ref,
|
|
|
+ () => {
|
|
|
+ // 返回值 是给父组件指定暴露内容
|
|
|
+ return {
|
|
|
+ getFile() {
|
|
|
+ return inputRef.current?.value;
|
|
|
+ },
|
|
|
+ start() {},
|
|
|
+ stop() {},
|
|
|
+ };
|
|
|
+ },
|
|
|
+ []
|
|
|
+ );
|
|
|
+ return <input type="file" ref={inputRef} />;
|
|
|
+}
|
|
|
+
|
|
|
+export default forwardRef(FileInput);
|
|
|
+// 父组件
|
|
|
+import FileInput from './FileInput';
|
|
|
+import { useRef } from 'react';
|
|
|
+
|
|
|
+export default function TestForwardRef() {
|
|
|
+ let fileRef = useRef(null);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <FileInput ref={fileRef} />
|
|
|
+ <button
|
|
|
+ onClick={() => {
|
|
|
+ console.log(fileRef.current.getFile());
|
|
|
+ // console.log(fileRef.current?.value);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 上传
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+```
|
|
|
|