Browse Source

react day4:其他hook

daxia 1 year ago
parent
commit
4b4b693eb2

+ 198 - 0
20_React.js_VIP22/React.js快速进坑.md

@@ -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>
+  );
+}
+```
 

+ 9 - 1
20_React.js_VIP22/day-4/code/react-hooks-demo/src/App.js

@@ -3,6 +3,10 @@ import TestUseState from './components/TestUseState';
 import Counter from './components/Counter';
 import TestUseEffect from './components/TestUseEffect';
 import Clock from './components/Clock';
+import TestUseCallback from './components/TestUseCallback';
+import RefsDOM from './components/RefsDOM';
+
+import TestForwardRef from './components/TestForwardRef';
 
 function App() {
   return (
@@ -11,7 +15,11 @@ function App() {
         {/* <TestUseState />
         <Counter /> */}
         {/* <TestUseEffect /> */}
-        <Clock />
+        {/* <Clock />
+
+        <TestUseCallback />
+        <RefsDOM /> */}
+        <TestForwardRef />
       </header>
     </div>
   );

+ 8 - 6
20_React.js_VIP22/day-4/code/react-hooks-demo/src/components/Clock.jsx

@@ -1,24 +1,26 @@
-import { useEffect, useState } from 'react';
+import { useEffect, useState, useRef } from 'react';
 
-let timer;
+// let timer;
 
 function Clock({ title }) {
   let [now, updateNow] = useState(new Date());
 
+  let timer = useRef(undefined);
+
   useEffect(() => {
     // 启动定时
-    timer = setInterval(() => {
+    timer.current = setInterval(() => {
       updateNow(new Date());
     }, 1000);
     // 返回函数,在组件卸载前清除定时器
     return () => {
-      console.log(timer);
-      clearInterval(timer);
+      // console.log(timer);
+      clearInterval(timer.current);
     };
   }, []);
 
   useEffect(() => {
-    console.log(timer);
+    console.log(timer.current);
   }, [title]);
 
   return (

+ 18 - 0
20_React.js_VIP22/day-4/code/react-hooks-demo/src/components/RefsDOM.jsx

@@ -0,0 +1,18 @@
+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;

+ 22 - 0
20_React.js_VIP22/day-4/code/react-hooks-demo/src/components/TestForwardRef/FileInput.jsx

@@ -0,0 +1,22 @@
+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);

+ 20 - 0
20_React.js_VIP22/day-4/code/react-hooks-demo/src/components/TestForwardRef/index.jsx

@@ -0,0 +1,20 @@
+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>
+  );
+}

+ 30 - 0
20_React.js_VIP22/day-4/code/react-hooks-demo/src/components/TestUseCallback.jsx

@@ -0,0 +1,30 @@
+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');
+  // }
+
+  const handleClick = useCallback(() => {
+    setMsg('heheihei');
+  }, []);
+
+  return (
+    <div>
+      <p>{msg}</p>
+      <MyButton onClick={handleClick}>按钮文字</MyButton>
+    </div>
+  );
+}
+
+export default TestUseCallback;