自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

React高手都善于使用useImprativeHandle

開發(fā) 前端
在 React Hooks 中,useImperativeHandle 是一個(gè)非常簡單的 Hook,他比較小眾,剛開始接觸 React 學(xué)習(xí)的朋友可能并不熟悉他。不過對于 React 頂尖高手而言,這是非常重要的 Hook,他能讓我們對 React 的使用變得更加得心應(yīng)手。應(yīng)對更多更復(fù)雜的場景。

一、useRef

學(xué)習(xí) useImperativeHandle,得從 useRef 說起。我們前面已經(jīng)學(xué)習(xí)過了 useRef,它能夠結(jié)合元素組件的 ref 屬性幫我們拿到該元素組件對應(yīng)的真實(shí) DOM。

例如,我想要拿到一個(gè) input 元素的真實(shí) DOM 對象,并調(diào)用 input 的 .focus() 方法,讓 input 獲得焦點(diǎn)。

import {useRef} from "react";

export default function Demo() {
  const inputRef = useRef<HTMLInputElement>(null);

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

  return (
    <>
      <input type="text" ref={inputRef} />
      <button onClick={focusTextInput}>
        點(diǎn)擊我讓input組件獲得焦點(diǎn)
      </button>
    </>
  );
}

每一個(gè) React 提供的元素組件,都具備 ref 屬性。在上面的章節(jié)中我們可以知道,當(dāng)我們拿到了元素的原生 DOM 對象之后,就可以脫離 React 的開發(fā)思路,從而應(yīng)對更多更復(fù)雜的場景。

那么問題就來了,原生組件有自己的 ref 屬性,那么自定義組件呢?當(dāng)然是沒有的,因此我們得自己想辦法處理。

二、forwardRef

forwardRef 能夠在我們自定義組件時(shí),把內(nèi)部組件的 ref 屬性傳遞給父組件。

它接受我們自定義的組件作為參數(shù),并返回一個(gè)新的組件。新組件具備我們自定義組件的全部能力,并得到一個(gè) ref 屬性,父組件通過 useRef 獲取到的內(nèi)容與內(nèi)部組件的 ref 完全一致。

我們來看一個(gè)案例。

現(xiàn)在我們要實(shí)現(xiàn)如下效果,當(dāng)點(diǎn)擊 Edit 按鈕時(shí),輸入框自動獲得焦點(diǎn)。

我們知道,在 DOM 中,只要得到 input 對象,然后就可以調(diào)用 .focus() 方法來實(shí)現(xiàn)目標(biāo)。現(xiàn)在我們要封裝一個(gè)自定義的 MyInput 組件,他具備 input 同樣的能力,同時(shí),我們還要封裝一個(gè)標(biāo)題進(jìn)去。

<label>Enter your name</label>
<input />

我們的代碼如下:

import {forwardRef, LegacyRef} from 'react'

type MyInputProps = React.InputHTMLAttributes<HTMLInputElement> & {
  label: string
}

function MyInput(props: MyInputProps, ref: LegacyRef<HTMLInputElement>) {
  const {label, ...other} = props

  return (
    <label>
      {label}
      <input {...other} ref={ref} />
    </label>
  )
}

export default forwardRef(MyInput)

MyInput 在聲明時(shí)要傳入兩個(gè)參數(shù),一個(gè) props,一個(gè) ref。通過展開運(yùn)算符,我們能夠確保 MyInput 支持 input 所有的屬性。

封裝好之后,我們就可以在點(diǎn)擊實(shí)踐中,通過 ref 得到的引用去調(diào)用 .focus() 達(dá)到 input 獲取焦點(diǎn)的目標(biāo)。

import { useRef } from 'react'
import MyInput from './MyInput'

export default function ImperativeHandle() {
  const ref = useRef<any>(null)

  function handleClick() {
    ref.current?.focus()
  }

  return (
    <form>
      <MyInput 
        label='Enter your name:' 
        ref={ref} 
      />
      <button type='button' onClick={handleClick}>Edit</button>
    </form>
  )
}

三、useImperativeHandle

在實(shí)踐中,很多時(shí)候,我們并不想通過 ref 去獲取子組件內(nèi)部的某個(gè)元素組件的真實(shí) DOM 對象。而是希望父組件能夠調(diào)用子組件內(nèi)部的某些方法

但是在 React 中,又無法直接 new 一個(gè)子組件的實(shí)例,像面向?qū)ο竽菢油ㄟ^子組件實(shí)例去調(diào)用子組件的方法。

因此,React 提供了一個(gè) hook,useImperativeHandle,讓我們能夠重寫子組件內(nèi)部 ref 對應(yīng)的引用,從而達(dá)到在父組件中,調(diào)用子組件內(nèi)部方法的目的

例如,上面的 MyInput 組件,我們可以修改代碼為:

import {forwardRef, useImperativeHandle, useRef} from 'react'

type MyInputProps = React.InputHTMLAttributes<HTMLInputElement> & {
  label: string
}

function MyInput(props: MyInputProps, ref: any) {
  const {label, ...other} = props
  const inputRef = useRef<any>(null)

  useImperativeHandle(ref, () => {
    return {
      focus() {
        inputRef.current.focus()
      }
    }
  }, [])

  return (
    <label>
      {label}
      <input {...other} ref={inputRef} />
    </label>
  )
}

export default forwardRef(MyInput)
useImperativeHandle(
  ref, 
  createHandle, 
  dependencies?
)

useImperativeHandle 接收三個(gè)參數(shù),分別是

  • ref: 組件聲明時(shí)傳入的 ref。
  • createHandle: 回調(diào)函數(shù),需要返回 ref 引用的對象,我們也是在這里重寫 ref 引用。
  • deps: 依賴項(xiàng)數(shù)組,可選。state,props 以及內(nèi)部定義的其他變量都可以作為依賴項(xiàng),React 內(nèi)部會使用 Object.is 來對比依賴項(xiàng)是否發(fā)生了變化。依賴項(xiàng)發(fā)生變化時(shí),createHandle 會重新執(zhí)行,ref 引用會更新。如果不傳入依賴項(xiàng),那么每次更新 createHandle 都會重新執(zhí)行。

useImperativeHandle 執(zhí)行本身返回 undefined。

四、官方案例

官方文檔中有這種一個(gè)案例,效果如圖所示。當(dāng)點(diǎn)擊按鈕時(shí),我希望下方的 input 自動獲得焦點(diǎn),并切中間的滾動條滾動到最底部。

現(xiàn)在,我們結(jié)合前面的知識來分析一下這個(gè)案例應(yīng)該如何實(shí)現(xiàn)。

首先我們先進(jìn)行組件拆分,將整個(gè)內(nèi)容拆分為按鈕部分與信息部分,信息部分主要負(fù)責(zé)信息的暫時(shí)與輸入,因此頁面組件大概長這樣。

<>
  <button>Write a comment</button>
  <Post />
</>

我們期望點(diǎn)擊按鈕時(shí),信息部分的輸入框自動獲取焦點(diǎn),信息部分的信息展示區(qū)域能滾動到最底部,因此整個(gè)頁面組件的代碼可以表示為如下:

import { useRef } from 'react';
import Post from './Post.js';

export default function Page() {
  const postRef = useRef(null);

  function handleClick() {
    postRef.current.scrollAndFocusAddComment();
  }

  return (
    <>
      <button onClick={handleClick}>
        Write a comment
      </button>
      <Post ref={postRef} />
    </>
  );
}

信息部分 Post 又分為兩個(gè)部分,分別是信息展示部分與信息輸入部分。

此時(shí)這兩個(gè)部分的 ref 要透傳給 Post,并最終再次透傳給頁面組件。

所以信息展示部分 CommentList 組件的代碼為。

import { forwardRef, useRef, useImperativeHandle } from 'react';

const CommentList = forwardRef(function CommentList(props, ref) {
  const divRef = useRef(null);

  useImperativeHandle(ref, () => {
    return {
      scrollToBottom() {
        const node = divRef.current;
        node.scrollTop = node.scrollHeight;
      }
    };
  }, []);

  let comments = [];
  for (let i = 0; i < 50; i++) {
    comments.push(<p key={i}>Comment #{i}</p>);
  }

  return (
    <div className="CommentList" ref={divRef}>
      {comments}
    </div>
  );
});

export default CommentList;

信息輸入部分 AddComment 的代碼為。

import { forwardRef, useRef, useImperativeHandle } from 'react';

const AddComment = forwardRef(function AddComment(props, ref) {
  return <input placeholder="Add comment..." ref={ref} />;
});

export default AddComment;

Post 要把他們整合起來。

import { forwardRef, useRef, useImperativeHandle } from 'react';
import CommentList from './CommentList.js';
import AddComment from './AddComment.js';

const Post = forwardRef((props, ref) => {
  const commentsRef = useRef(null);
  const addCommentRef = useRef(null);

  useImperativeHandle(ref, () => {
    return {
      scrollAndFocusAddComment() {
        commentsRef.current.scrollToBottom();
        addCommentRef.current.focus();
      }
    };
  }, []);

  return (
    <>
      <article>
        <p>Welcome to my blog!</p>
      </article>
      <CommentList ref={commentsRef} />
      <AddComment ref={addCommentRef} />
    </>
  );
});

export default Post;

這樣,我們整個(gè)案例的代碼就寫完了。useRef、useImprativeHandle、forwardRef 一起配合幫助我們完成了這個(gè)功能。

責(zé)任編輯:姜華 來源: 這波能反殺
相關(guān)推薦

2013-03-18 09:30:14

大數(shù)據(jù)IT

2020-05-29 10:18:58

python開發(fā)代碼

2023-12-20 14:48:26

2024-01-16 08:43:51

React底層機(jī)制Hook

2024-02-05 21:48:25

VueReactHooks

2018-05-25 15:10:14

360手機(jī)衛(wèi)士

2011-05-26 10:04:30

程序員

2009-07-16 13:51:43

2024-02-26 12:10:37

2022-04-08 10:15:29

VueReacHooks

2022-04-18 17:28:14

React前端

2024-01-25 09:04:25

2018-03-15 09:07:37

手機(jī)垃圾文件清理

2024-02-01 09:05:30

ContextReact性能優(yōu)化

2023-11-09 16:20:32

Vue.jsReact前端

2009-04-15 10:49:01

木馬釋放器卡巴斯基

2011-05-16 16:59:41

SEO

2019-11-07 21:41:21

AndroidiOS不同

2024-12-06 08:00:51

2009-12-29 15:32:01

架構(gòu)師
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號