VUE3.0 をすぐに始める方法: 入門を入力して学習する
プロジェクトでは、 Ref
が必要となるシナリオが数多くあります。たとえば、 ref
属性を使用して DOM ノードを取得し、ClassComponent オブジェクトのインスタンスを取得します。setInterval setInterval
最新の状態を取得できない問題を解決するには、 useRef
フックを使用してReact.createRef
を呼び出すこともできます。 Ref
オブジェクトを手動で作成するメソッド。
Ref
は非常に簡単に使用できますが、実際のプロジェクトでは依然として問題が発生することは避けられません。この記事では、 Ref
に関連するさまざまな問題をソース コードの観点から整理し、 ref
関連の API の背後で何が行われているかを明らかにします。この記事を読むと、 Ref
についての理解がさらに深まるかもしれません。
まず、 ref
reference
の略称であり、参照です。 react
型宣言ファイルには、いくつかの Ref 関連の型があり、それらはここにリストされています。
インターフェイス RefObject<T> { 読み取り専用 現在: T null }違いは
、
useRef
RefObject
{ current: T }
プロパティが読み取り専用であることです。 , Typescript は、 refObject.current
が変更されると、⚠️ と警告します。
const ref = useRef<string>(null) ref.current = '' // エラー
TS: 「current」は読み取り専用プロパティであるため、割り当てることができません。
useRef
メソッドの定義を見てください。ここでは関数のオーバーロードが使用されています。受信ジェネリック パラメーターT
にnull
が含まれていない場合は、 RefObject<T>
が返されます。null null
含まれている場合は、 MutableRefObject<T>
が返されます。
関数 useRef<T>(初期値: T): MutableRefObject<T>; function useRef<T>(initialValue: T | null): RefObject<T>;
したがって、作成された ref オブジェクトの現在のプロパティを変更可能にしたい場合は、 | null
追加する必要があります。
const ref = useRef<文字列>(null) ref.current = '' // OK、
React.createRef()
メソッドを呼び出すと、 RefObject
も返されます。
createRef
エクスポート関数 createRef(): RefObject { const refObject = { 現在: null、 }; if (__DEV__) { Object.seal(refObject); } refObject を返します。 RefObject
RefObject/MutableRefObject
バージョン16.3
で追加されました。以前のバージョンを使用する場合は、 Ref Callback
使用する必要があります。
Ref Callback
を使用すると、react がコールバックするときに、対応するインスタンスが返され、呼び出し用にそれ自体を保存できます。このコールバック関数の型はRefCallback
です。
type RefCallback<T> = (instance: T | null) => void
RefCallback
の使用例:
import React from 'react' エクスポートクラス CustomTextInput extends React.Component { textInput: HTMLInputElement null = null; saveInputRef = (要素: HTMLInputElement | null) => { this.textInput = 要素; } 与える() { 戻る ( <input type="text" ref={this.saveInputRef} /> ); }
型宣言には、Ref 型を一般に参照するために使用される Ref/LegacyRef 型もあり
。
LegacyRef
は互換性のあるバージョンです。以前の古いバージョンでは、 ref
文字列である可能性もありました。
型 Ref<T> = RefCallback<T> | null; type LegacyRef<T> = string | Ref<T>;
Ref に関連する型を理解することによってのみ、Typescript をより快適に作成できるようになります。
JSX コンポーネントでref
を使用する場合、 Ref
ref
属性に設定します。 jsx
の構文が Babel などのツールによってcreateElement
の形式にコンパイルされることは誰もが知っています。
//jsx <アプリ ref={ref} id="my-app" ></アプリ> // にコンパイルされる React.createElement(App, { 参照: 参照、 ID:「私のアプリ」ref
は
ref
の props と変わらないように見えますが、コンポーネント内で props.ref を出力しようとすると、 undefined
になります。そして、 dev
環境コンソールにプロンプトが表示されます。
子コンポーネント内で同じ値にアクセスする必要がある場合は
undefined
それを別の prop として渡す必要があります。React
は ref で何をしますか? ReactElement のソース コードでわかるように、 ref
RESERVED_PROPS
です。 key
もこの処理を行い、props から抽出され、 Element
に渡されます。
const RESERVED_PROPS = { キー: true、 参照:本当、 __自分: 本当です、 __ソース: 本当、したがって
、
ref
特別に扱われる“props“
です。
バージョン16.8.0
より前では、関数コンポーネントはステートレスであり、受信した props に基づいてのみレンダリングされていました。 Hook を使用すると、内部状態を保持できるだけでなく、外部呼び出しのメソッドを公開することもできます ( forwardRef
とuseImperativeHandle
が必要)。
Function Component
にref
直接使用すると、開発環境のコンソールは、それをforwardRef
でラップする必要があることを警告します。
functionInput() { <入力> を返す } const ref = useRef() <Input ref={ref} />
関数コンポーネントに ref を指定できません。この ref にアクセスしようとすると失敗します。forwardRef() とは
forwardRef
ですか?ソース コード ReactForwardRef.js を表示します。 __DEV__
関連のコードを折りたたみます。これは非常に単純な高位コンポーネントです。レンダリングされた FunctionComponent を受け取り、ラップして$$typeof
REACT_FORWARD_REF_TYPE
として定義してreturn
。
コードをトレースして、resolveLazyComponentTag を見つけます。 $$typeof
は、対応する WorkTag に解析されます。
REACT_FORWARD_REF_TYPE
に対応する WorkTag は ForwardRef です。次に、ForwardRef は updateForwardRef のロジックに入ります。
case ForwardRef: { 子 = updateForwardRef( ヌル、 作業中、 成分、 解決された小道具、 レンダーレーン、 ); 帰国子女。このメソッド
は
renderWithHooks メソッドを呼び出し、5 番目のパラメータにref
を渡します。
nextChildren = renderWithHooks( 現在、 作業中、 与える、 次の小道具、 ref, // ここでは renderLanes, );
コードのトレースを続けて、 renderWithHooks メソッドに入ります。 ref
Component
の 2 番目のパラメーターとして渡されることがわかります。この時点で、 forwardRef
によってラップされたFuncitonComponent
の 2 番目のパラメーターref
どこから来たのかを理解できます (Context である ClassComponent コンストラクターの 2 番目のパラメーターと比較して)。
ref を渡す方法がわかったら、次の疑問は ref がどのように割り当てられるかです。
(refにRefCallbackを代入してコールバック内でブレークする) このメソッドでは、Fibreノードのrefがfunction
かRefObjectかを判定します。種類に応じて加工させていただきます。ファイバー ノードが DOM ノードである HostComponent ( tag = 5
) である場合、instance は DOM ノードであり、ファイバー ノードが ClassComponent ( tag = 1
) である場合、instance はオブジェクト インスタンスです。
関数 commitAttachRef(finishedWork) { var ref = 完了した作業.ref; if (ref !== null) { varinstanceToUse = completedWork.stateNode; if (typeof ref === '関数') { ref(使用するインスタンス); } それ以外 { ref.current = 使用するインスタンス; } }上記は
、
HostComponent と ClassComponent の ref の割り当てロジックです。ForwardRef タイプのコンポーネントでは、異なるコードが使用されますが、動作は基本的に同じです。
次に、引き続き React ソース コードを調べて、useRef がどのように実装されているかを確認します。
コードを追跡することで useRef ランタイム コード ReactFiberHooks を見つけます。
ここには、 mountRef
とupdateRef
2 つのメソッドがあります。名前が示すように、これらはFiber
ノードmount
およびupdate
ときのref
の操作に対応します。
関数 updateRef<T>(初期値: T): {|現在: T|} { const フック = updateWorkInProgressHook(); フック.memoizedStateを返します。 } 関数 mountRef<T>(初期値: T): {|現在: T|} { const フック = mountWorkInProgressHook(); const ref = {現在: 初期値}; フック.memoizedState = ref; 参照を返します。
mount
時にuseRef
RefObject
作成し、それをhook
のmemoizedState
に割り当てていることがわかります。 update
時にはそれが取り出されて直接返されます
。
さまざまなフック memoizedState はさまざまstate
コンテンツを保存し、 useEffect
effect
オブジェクトを保存し、 useRef
ref
オブジェクトを保存しますuseState
mountWorkInProgressHook
とupdateWorkInProgressHook
メソッドは、フックのリンク リストに基づいています。 useRef をレンダリングするたびに同じ memoizedState オブジェクトを取得するのはとても簡単です。
この時点で、React でref
渡して代入するロジックと、 useRef
に関連するソース コードを理解しました。アプリケーションの質問を使用して、上記の知識ポイントを統合します。 コンポーネント内には、innerRef HTMLInputElement
使用してDOM
ノードにアクセスする必要があります。同時に、外部コンポーネントがノードを参照することもできます。それを実装するには?
const 入力 = forwardRef((props, ref) => { const innerRef = useRef<HTMLInputElement>(null) 戻る ( <input {...props} ref={???} /> ) })
上記のコードの???
どのように書くべきかを考えてください。
============ 答えの分割線 ==============
Ref に関連する内部実装を理解することで、ここでRefCallback
作成できることは明らかです。複数のref
割り当てるだけで処理できます。
エクスポート関数combineRefs<T = any>( refs: 配列<MutableRefObject<T | RefCallback<T>> ): React.RefCallback<T> { 戻り値 => { refs.forEach(ref => { if (typeof ref === '関数') { 参照(値); else if (ref !== null) { 参照電流 = 値; } }); }; } const 入力 = forwardRef((props, ref) => { const innerRef = useRef<HTMLInputElement>(null) 戻る ( <input {...props} ref={combineRefs(ref, innerRef)} /> ) })