VUE3.0을 빠르게 시작하는 방법:
React 프로젝트에는 Ref
가 필요한 시나리오가 많이 있습니다. 예를 들어, ref
속성을 사용하여 DOM 노드를 얻고 ClassComponent 객체 인스턴스를 얻습니다. setInterval
최신 상태를 얻을 수 없는 문제를 해결하기 위해 useRef
후크를 사용하면 React.createRef
를 호출할 수도 있습니다. Ref
객체를 수동으로 생성하는 방법.
Ref
는 사용하기 매우 간단하지만 실제 프로젝트에서는 여전히 문제가 발생할 수 있습니다. 이 기사에서는 소스 코드 관점에서 Ref
와 관련된 다양한 문제를 정리하고 ref
관련 API에서 수행되는 작업을 명확하게 설명합니다. 이 글을 읽고 나면 Ref
에 대해 더 깊이 이해할 수 있을 것입니다.
우선 ref
는 참조인 reference
의 약어입니다. react
유형 선언 파일에서 여러 Ref 관련 유형을 찾을 수 있으며 여기에 나열되어 있습니다.
인터페이스 RefObject<T> { 읽기 전용 현재: T | 인터페이스 MutableRefObject<T> { current: T; }
{ current: T }
useRef
을 사용하면 RefObject/MutableRefObejct가 반환됩니다. 두 유형 모두 RefObject
의 현재 속성이 읽기 전용이라는 점입니다. , Typescript는 refObject.current
수정되면 ⚠️ 경고합니다.
const ref = useRef<문자열>(널) ref.current = '' // 오류
TS: "current"는 읽기 전용 속성이므로 할당할 수 없습니다.
useRef
메서드의 정의 를 살펴보세요. 수신되는 일반 매개변수 T
null
null
되어 있으면 MutableRefObject<T>
RefObject<T>
반환됩니다.
함수 useRef<T>(initialValue: T): MutableRefObject<T>; function useRef<T>(initialValue: T | null): RefObject<T>;
따라서 생성된 ref 객체의 현재 속성을 수정 가능하게 하려면 | null
const ref = useRef<문자열 null>(널) ref.current = '' // 좋습니다.
React.createRef()
메서드를 호출하면 RefObject
도 반환됩니다.
createRef
내보내기 함수 createRef(): RefObject { const refObject = { 현재: null, }; 만약 (__DEV__) { Object.seal(refObject); } refObject를 반환합니다. }
RefObject/MutableRefObject
버전 16.3
에서 추가되었습니다. 이전 버전을 사용하는 경우 Ref Callback
사용해야 합니다.
Ref Callback
사용하는 것은 콜백 함수를 전달하는 것입니다. React가 콜백할 때 해당 인스턴스가 다시 전달되며 호출을 위해 자체적으로 저장할 수 있습니다. 이 콜백 함수의 유형은 RefCallback
입니다.
type RefCallback<T> = (인스턴스: T | null) =>
RefCallback
사용 예 :
import React from 'react';
내보내기 클래스 CustomTextInput은 React.Component를 확장합니다. 텍스트 입력: HTMLInputElement null = null; saveInputRef = (요소: HTMLInputElement | null) => { this.textInput = 요소; } 렌더링() { 반품 ( <input type="text" ref={this.saveInputRef} /> ); } }
타입 선언에는 일반적으로 Ref 타입을 참조하는 데 사용되는 Ref/LegacyRef 타입도 있습니다. LegacyRef
는 호환되는 버전입니다. 이전 버전에서는 ref
문자열일 수도 있습니다.
유형 Ref<T> = RefCallback<T> | RefObject<T> | type LegacyRef<T> = string | Ref<T>;
Ref와 관련된 유형을 이해해야만 Typescript 작성에 더 익숙해질 수 있습니다.
JSX 컴포넌트에서 ref
사용할 때 Ref
ref
속성으로 설정합니다. 우리 모두는 jsx
의 구문이 Babel과 같은 도구를 통해 createElement
형식으로 컴파일된다는 것을 알고 있습니다.
//jsx <App ref={ref} id="my-app" ></App> // 컴파일됨 React.createElement(앱, { 참조: 참조, ID: "내 앱" });
ref
다른 props와 다르지 않은 것 같지만, 컴포넌트 내부에서 props.ref를 출력하려고 하면 undefined
이 됩니다. 그리고 dev
환경 콘솔에 프롬프트가 표시됩니다.
이에
액세스하려고 하면
undefined
이 반환됩니다. 하위 구성 요소 내에서 동일한 값에 액세스해야 하는 경우 이를 다른 prop으로 전달해야 합니다.
ReactElement 소스 코드에서 볼 수 있듯이 ref
RESERVED_PROPS
입니다. key
에도 이 처리가 적용되며 props에서 추출되어 Element
로 전달됩니다.
const RESERVED_PROPS = { 키: 사실, 심판: 사실, __self: 사실, __출처: 사실, };
따라서 ref
특별히 취급되는 “props“
입니다.
버전 16.8.0
이전에는 Function Component가 Stateless였으며 들어오는 Prop을 기반으로만 렌더링했습니다. Hook를 사용하면 내부 상태를 가질 수 있을 뿐만 아니라 외부 호출에 대한 메서드도 노출할 수 있습니다( forwardRef
및 useImperativeHandle
필요).
Function Component
에 대해 ref
직접 사용하는 경우 개발 환경의 콘솔은 이를 forwardRef
로 래핑해야 한다는 경고를 표시합니다.
함수입력() { <입력 /> 반환 } const ref = useRef() <Input ref={ref} />
함수 구성 요소에 참조를 제공할 수 없습니다. 이 참조에 액세스하려는 시도가 실패합니다. React.forwardRef()가
forwardRef
입니까? 소스코드 ReactForwardRef.js를 보세요. __DEV__
관련 코드를 접으면 아주 단순한 고차 컴포넌트입니다. 렌더링된 FunctionComponent를 받아 래핑하고 $$typeof
REACT_FORWARD_REF_TYPE
으로 정의한 후 return
.
코드를 추적하고 $$typeof
해당 WorkTag로 구문 분석되는 해결LazyComponentTag를 찾으세요.
REACT_FORWARD_REF_TYPE
에 해당하는 WorkTag는 ForwardRef입니다. 그런 다음 ForwardRef는 updateForwardRef의 논리를 입력합니다.
케이스 전달참조: { 자식 = 업데이트ForwardRef( 널, 작업진행중, 요소, 해결된 소품, 렌더레인, ); 아이를 돌려보내다; }
이 메소드는 renderWithHooks 메소드를 호출하고 다섯 번째 매개변수에 ref
를 전달합니다.
nextChildren = renderWithHooks( 현재의, 작업진행중, 세우다, 다음속성, ref, // 여기서는 renderLanes, );
계속해서 코드를 추적하고 renderWithHooks 메소드를 입력하면 ref
Component
의 두 번째 매개변수로 전달되는 것을 볼 수 있습니다. 이 시점에서 우리는 (Context인 ClassComponent 생성자의 두 번째 매개변수와 비교하여) forwardRef
로 래핑된 FuncitonComponent
의 두 번째 매개변수 ref
어디에서 왔는지 이해할 수 있습니다.
ref를 전달하는 방법을 알고 나면 다음 질문은 ref를 할당하는 방법입니다.
(RefCallback을 ref에 할당하고 콜백의 지점을 중단함) commitAttachRef 코드를 추적하면 Fiber 노드의 참조가 function
인지 RefObject인지 판단됩니다. 종류에 따라 처리됩니다. Fiber 노드가 DOM 노드인 HostComponent( tag = 5
)인 경우 인스턴스는 DOM 노드이고 Fiber 노드가 ClassComponent( tag = 1
)인 경우 인스턴스는 객체 인스턴스입니다.
함수 commitAttachRef(finishedWork) { var ref = doneWork.ref; if (ref !== null) { varinstanceToUse=finishedWork.stateNode; if (ref 유형 === '함수') { ref(instanceToUse); } 또 다른 { ref.current = 인스턴스투사용; } } }
위는 HostComponent와 ClassComponent의 ref 할당 논리입니다. ForwardRef 유형 컴포넌트의 경우 다른 코드가 사용되지만 동작은 기본적으로 여기에서 볼 수 있습니다.
다음으로, useRef가 어떻게 구현되는지 알아보기 위해 React 소스 코드를 계속해서 살펴보겠습니다.
코드를 추적하여 useRef 런타임 코드 ReactFiberHooks를 찾습니다.
여기에는 mountRef
및 updateRef
두 가지 메소드가 있습니다. 이름에서 알 수 있듯이 Fiber
노드가 mount
되고 update
될 때 ref
에 대한 작업에 해당합니다.
함수 updateRef<T>(initialValue: T): {|현재: T|} { const 후크 = updateWorkInProgressHook(); Hook.memoizedState를 반환합니다. } 함수 mountRef<T>(initialValue: T): {|현재: T|} { const 후크 = mountWorkInProgressHook(); const ref = {현재: 초기값}; Hook.memoizedState = ref; 참조 반환; }
mount
할 때 useRef
RefObject
를 생성하고 이를 hook
의 memoizedState
에 할당하는 것을 볼 수 있습니다. update
할 때 이를 꺼내어 직접 반환합니다.
다른 Hook memoizedState는 다른 내용을 저장합니다. useState
state
정보를 저장하고, useEffect
effect
개체를 저장하고, useRef
ref
개체를 저장합니다.
mountWorkInProgressHook
및 updateWorkInProgressHook
메서드는 연결된 목록이 수정되지 않은 경우 다음을 수행할 수 있습니다. useRef를 렌더링할 때마다 동일한 memoizedState 객체를 검색합니다. 매우 간단합니다.
지금까지 우리는 React에서 ref
전달하고 할당하는 논리와 useRef
와 관련된 소스 코드를 이해했습니다. 위의 지식 포인트를 통합하려면 애플리케이션 질문을 사용하십시오. 구성 요소 내부에는 innerRef HTMLInputElement
사용하여 DOM
노드에 액세스해야 합니다. 그것을 구현하려면?
const 입력 = 전달Ref((props, ref) => { const innerRef = useRef<HTMLInputElement>(null) 반품 ( <입력 {...props} ref={???} /> ) })
위 코드에서 ???
어떻게 작성해야 하는지 생각해 보세요.
============ 구분선에 답하기 ===============
Ref와 관련된 내부 구현을 이해하면 여기에서 RefCallback
생성할 수 있다는 것이 분명해집니다. 여러 개를 처리할 수 있습니다. ref
를 할당하면 됩니다.
내보내기 함수 CombineRefs<T = any>( 참조: 배열<MutableRefObject<T | null> RefCallback<T>> ): React.RefCallback<T> { 반환 값 => { refs.forEach(ref => { if (ref 유형 === '함수') { 심판(값); } else if (ref !== null) { 참조전류 = 값; } }); }; } const 입력 = 전달Ref((props, ref) => { const innerRef = useRef<HTMLInputElement>(null) 반품 ( <입력 {...props} ref={combineRefs(ref, innerRef)} /> ) })