React 函數式組件的 Ref 和子組件訪問(useImperativeHandle)


引入:如何調用函數式組件內部的方法

對於 React 中需要強制修改子組件的情況,React 提供了 Refs 這種解決辦法,使得我們可以操作底層 DOM 元素或者自定的 class 組件實例。除此之外,文檔(v17.0.1)對函數式組件另有描述:

不能在函數式組件上使用ref屬性,因為他們沒有實例

在函數式組件和 Hooks 大面積普及的現在,這個特性沒有完全對標 class 組件,令人疑惑。不過經過一陣探索和請教,發現確實是有對應的解決方案的:

useImperativeHandle

結合 React.forwardRefuseImperativeHandle 文檔 應該就能明白是如何使用的。

簡而言之就是可以在函數式組件上使用 ref,通過useImperativeHandle這個hook可以指定暴露給父組件的值和函數。

案例:

修改子組件Counter中的值, 達到重置count的目的:

export default function App() {
  return (
    <div>
      <button>reset</button>
      <Counter />
    </div>
  );
}
/** -------------------------------------- */
function Counter() {
  const [count, setCount] = useState(0);
  function increment() {
    setCount(count + 1);
  }
  return (
    <div>
      <hr />
      <span>{count}</span>
      <button onClick={increment}>+1</button>
    </div>
  );
}

對於這個案例,將count這個state往上提一層到 App 組件中是比較合適的,但是在這里重點討論操作子組件

使用useImperativeHandle,修改代碼:

export default function App() {
  const counterRef = useRef();
  function reset() {
    counterRef.current?.resetCount();
  }
  return (
    <div style={{ padding: 10 }}>
      <button onClick={reset}>reset</button>
      <MyCounter ref={counterRef} />
    </div>
  );
}
/** -------------------------------------- */
function Counter(props, ref) {
  const [count, setCount] = useState(0);
  useImperativeHandle(ref, () => ({
    resetCount: resetCount,
  }));
  function resetCount() {
    setCount(0);
  }
  function increment() {
    setCount(count + 1);
  }
  return (
    <div>
      <hr />
      <span>{count}</span>
      <button onClick={increment}>+1</button>
    </div>
  );
}
const MyCounter = React.forwardRef(Counter);

重點是useImperativeHandle中定義了resetCount,以及使用React.forward獲取 ref,在App組件中為MyCounter中定義ref屬性,然后就可以在外部父組件中使用通過ref調用子組件的resetCount方法。

到這里,實際上已經達到了和classref對等的效果。通過給函數式組件設置 ref 並調用組件的方法是可行的,useImperativeHandle除了添加方法,也可以指定值暴露出去。

函數式組件的Ref是什么

將 ref 設置到 HTML 元素上,獲取的是對應的DOM元素,如span:

設置到 class 組件上,獲取的是 class 組件實例:

設置到函數式組件上的時候,獲取的是一個包含可變值或函數的對象,如上例的 Counter 組件:

React.createRefuseRef 都是創建了一個包含current屬性的對象,綁定ref時,對應的屬性和函數都在current對應的對象中。

查看對應的TypeScript類型,React.createRef創建的是React.RefObject類型,是只讀的。

useRef創建的是React.MutableRefObject,是可讀寫的。可以保存任何可變的值,使用方式類似於class組件的this實例變量。(又是和class組件對標的一個點)

文檔描述 useRef 為可以在其.current屬性中保存一個可變值的“盒子”。

所以實際上應該是,對函數式組件可設置 ref,且設置的 ref 是一個可變對象,存放組件的變量,也能通過useImperativeHandle訪問函數式組件的方法。 但是並不能像將 ref 設置到 class 組件和 DOM 元素上那樣獲取到對應的實例。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM