Day4-React Hook 篇-认识 useRef & useImperativeHandle

今天要介绍的是可以用来操作 DOM 元素的 useRef 及和它有关的 useImperativeHandle。

功用

  1. 操作 DOM 元素(ex: 管理 focus、选择文字)
  2. 取得先前的 state 值
    https://zh-hant.reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state

语法和范例

useRef 只会回传一个值,这个值是一个有 current 属性的物件。

import React, { useEffect, useRef } from 'react';

const App = () => {
  const h1Ref = useRef(); // 定义一个 ref
	
  useEffect(() => {
    console.log(h1Ref.current); // 使用 ref 名字.current 取出 dom 元素
  }, [])
	
	// 绑定 ref 到 dom 元素上	
  return <h1 ref={h1Ref}>Hello World!</h1>
}
export default App;

focus input 输入框

import { useState, useRef, useEffect } from "react";

export default function App() {
  const inputRef = useRef();

  const clickHandler = () => {
    inputRef.current.focus();
  };

  return (
    <>
      <input type="text" ref={inputRef} />
      <button onClick={clickHandler}>Focus</button>
    </>
  );
}

程序码范例(codesandbox)

特性

更新 current 值并不会触发 re-render

其他用途

可以用来避免元件第一次 render 时,useEffect 内的程序码执行

react-hooks: skip first run in useEffect

import { useState, useRef, useEffect } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  const isFirstRun = useRef(true);
  useEffect(() => {
    // 第一次执行 useEffect 就直接 return 掉,不执行後面的程序码
    if (isFirstRun.current) {
      isFirstRun.current = false;
      return;
    }

    console.log("Effect was run");
  });

  return (
    <div>
      <p>Clicked {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

2021/10/22 补充

如果要取得 ref 取到的 DOM 元素底下的子元素,可以加上 children,如范例所示:

useEffect(() => {
    console.log(tableRef.current); // table
    console.log(tableRef.current.children[0]); // thead
    console.log(tableRef.current.children[1]); // tbody
    console.log(tableRef.current.children[0].children); // thead tr
    ...
}, [])

forwardRef

将 ref 传递到子元件,让子元件可以取到父元件的 DOM。

范例

App.js

import { useRef } from "react";
import Child from "./Child";

const App = () => {
  const textInput = useRef(null);

  function handleClick() {
    textInput.current.focus();
  }
  return (
    <div>
      <Child ref={textInput} />
      <input type="button" value="Focus the text input" onClick={handleClick} />
    </div>
  );
};

export default App;

Child.js

import { forwardRef } from "react";

const Child = forwardRef((props, ref) => {
  console.log(ref); // <input type="text"></input>

  return <input type="text" ref={ref} />;
});

export default Child;

程序码范例(codesandbox)

inputRef.current.focus();

参考 https://zh-hant.reactjs.org/docs/refs-and-the-dom.html

useImperativeHandle

此 hook 会和 forwardRef 一起使用,可以将子元件内的 ref 内置的方法或是值传递到母元件,此 hook 较不常使用。

语法

useImperativeHandle(ref, createHandle, [deps])
第二个参数是一个函式,会把里面的东西绑定到 ref 上,第三个参数作用同 useEffect 的第二个参数,当值改变时重新执行第二个参数

范例

在范例中,透过 useImperativeHandle 将 verify 和 validate 两个函式从 TextInput 元件传到 App 元件

程序码范例(codesandbox)


<<:  【第四天 - Flutter BottomNavigationBar(上)Animation】

>>:  从0到1 from zero to one 心得 发现1 看待未来的四种方法 #内文

应用 LINE Front-end Framework 轻松建立互动 (3)

今天继续搭配 LIFF 的文件研究 Line LIFF App line-liff-v2-start...

威胁建模-DREAD

-Stride、VAST、Trike 等:哪种威胁建模方法适合您的组织? 风险敞口是根据可能性、後...

React Hooks - useEffect

useEffect() 让我们在 function component 内也可以实现 class ...

【day25】上传多张照片(下)

连假第二天,在这边先祝大家连假快乐啦,那延续昨天,我们现在已经把String的List拿到了,现在...

#4 - The Global Object &Function Expressions

今天来讲讲两个之後会用到的两个小观念: The Global Object &Function Ex...