Привет всем, я Mokou, я давно не пузырился, и я недавно читал об алгоритмах исследования и структурах данных, но, похоже, многие фронтенды не любят читать такие вещи. , а еще я в данный момент разочаровался в алгоритмах, так что не буду смущаться. .
Конечно, недавно я тоже начал изучать React.Эта статья в основном о контенте, связанном с Ref.Пожалуйста, поправьте меня, если я ошибаюсь.
Происхождение ссылки
В типичном потоке данных React свойства — это единственный способ взаимодействия родительского компонента с дочерним компонентом. Чтобы изменить дочерний компонент, вам нужно повторно отобразить его с новыми реквизитами. Однако в некоторых случаях вам необходимо принудительно изменить дочерние компоненты/элементы за пределами типичного потока данных.
Когда уместно использовать ссылки:
- Управляйте фокусом, выделением текста или воспроизведением мультимедиа.
- Запустить принудительную анимацию.
- Дом объединяет сторонние библиотеки.
Четыре пути REF
До React v16.3 ref получали как строку (string ref) или функцию обратного вызова (callback ref).
ref получается по символу:
// string ref
class MyComponent extends React.Component {
componentDidMount() {
this.refs.myRef.focus();
}
render() {
return <input ref="myRef" />;
}
}
ref получается через функцию обратного вызова:
// callback ref
class MyComponent extends React.Component {
componentDidMount() {
this.myRef.focus();
}
render() {
return <input ref={(ele) => {
this.myRef = ele;
}} />;
}
}
В версии 16.30017-new-create-refВ предложении представлены новые API:React.createRef
.
ref получается через React.createRef:
// React.createRef
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
this.myRef.current.focus();
}
render() {
return <input ref={this.myRef} />;
}
}
Конечно, есть и хуки, которые недавно получили высокую оценку: useRef
function MyComponent() {
const myRef = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
myRef.current.focus();
};
return (
<>
<input ref={myRef} type="text" />
<button onClick={onButtonClick}>聚焦</button>
</>
);
}
ссылка на строку, которая будет удалена
Во-первых, поговорим оstring ref
, string ref уже давно подвергается критике, а в официальной документации React говорится:"如果你目前还在使用 this.refs.textInput 这种方式访问 refs ,我们建议用回调函数或 createRef API 的方式代替。"
, почему так плохо?
Первоначально написано одним из авторов Reactdan abramov
. опубликованоnews.vs.com, чем Nator.com/edit?ID=120…, (на площадке требуется лестница). Основное содержание разглагольствований следующее:
- ссылки на строки не являются составными. Например, родительский компонент сторонней библиотеки передал ref дочернему компоненту, тогда мы не можем добавить ref в дочерний компонент. С другой стороны, у ссылок обратного вызова нет владельца, поэтому вы можете написать их, когда захотите. Например:
/** string ref **/
class Parent extends React.Component {
componentDidMount() {
// 可获取到 this.refs.childRef
console.log(this.refs);
}
render() {
const { children } = this.props;
return React.cloneElement(children, {
ref: 'childRef',
});
}
}
class App extends React.Component {
componentDidMount() {
// this.refs.child 无法获取到
console.log(this.refs);
}
render() {
return (
<Parent>
<Child ref="child" />
</Parent>
);
}
}
- Владелец строки ref определяется исполняемым в данный момент компонентом. Это означает, что с общим шаблоном «обратный вызов рендеринга» (например, реакция) неправильный компонент будет иметь ссылку (в конечном итоге он определит renderRow для реакции вместо вашего компонента).
class MyComponent extends Component {
renderRow = (index) => {
// string ref 会挂载在 DataTable this 上
return <input ref={'input-' + index} />;
// callback ref 会挂载在 MyComponent this 上
return <input ref={input => this['input-' + index] = input} />;
}
render() {
return <DataTable data={this.props.data} renderRow={this.renderRow} />
}
}
- string ref не подходит для статического анализа, такого как Flow. Flow не может угадать магию, с помощью которой фреймворк может заставить строковую ссылку «появляться» в React, и какого она типа (которая может варьироваться). Ссылки обратного вызова удобнее, чем статический анализ.
- string ref заставляет React отслеживать исполняемый в данный момент компонент. Это проблематично, потому что это делает модуль реакции сохраняющим состояние и вызывает странные ошибки при копировании модуля реакции в пакете. На этапе согласования, в процессе создания и обновления элемента React, ref будет инкапсулирован как функция закрытия, ожидающая выполнения этапа фиксации, что окажет некоторое влияние на производительность React.
Для этого вы можете обратиться к исходному коду React.coerceRef
Реализация:
В процессе сверки дочерних узлов будет обработана строка ref и преобразована в метод, главное в этом методе установить instance.refs[stringRef] = element, что эквивалентно преобразованию его в функцию ref
Нужно ли сравнивать строку ref во время процесса обновления с current.ref._stringRef, который записывает значение строки ref, если оно использовалось при последнем рендеринге.
Владелец получается при вызове createElement, а получается через ReactCurrentOwner.current.Это значение будет установлено перед обновлением компонента.Например, при обновлении ClassComponent оно будет установлено перед вызовом метода рендера, и тогда соответствующее значение может быть полученный при вызове render.owner.
Сильная ссылка обратного вызова
React вызовет обратный вызов ref с элементом DOM, когда компонент смонтирован, и вызовет его с нулевым значением, когда он будет размонтирован. React обеспечит актуальность ссылок до того, как сработает componentDidMount или componentDidUpdate.
Если функция обратного вызова REF определена таким образом в функции завершения, она будет выполняться дважды в процессе обновления, и сначала передается первый входящий параметр null, а затем снова передается элемент DOM параметра. Это связано с тем, что при каждом рендеринге создается новый экземпляр функции, поэтому React очищает старый REF и устанавливает новый. Вышеупомянутых проблем можно избежать, определив функцию обратного вызова REF для функции привязки класса, но в большинстве случаев это не имеет значения.
позже React.createRef
Преимущества React.createRef:
- React.createRef более интуитивно понятен, чем ссылка обратного вызова, что позволяет избежать некоторых проблем понимания ссылки обратного вызова.
Недостатки React.createRef:
- Производительность немного ниже, чем у callback ref
- Эта возможность все еще уступает callback ref.Например, проблема комбинации, упомянутая в предыдущем разделе, createRef также бессильна.
Значение Ref варьируется в зависимости от типа узла:
- Когда атрибут ref используется в элементе HTML, ссылка, созданная с помощью React.createRef() в конструкторе, получает базовый элемент DOM в качестве своего текущего атрибута.
- Когда атрибут ref используется для компонента пользовательского класса, объект ref получает смонтированный экземпляр компонента в качестве своего текущего атрибута.
- По умолчанию вы не можете использовать атрибуты ref для функциональных компонентов (вы можете использовать их внутри функциональных компонентов), потому что у них нет экземпляров:
- Если вы хотите использовать ref в функциональном компоненте, вы можете использовать forwardRef (можно комбинировать с useImperativeHandle)
- Или вы можете превратить компонент в компонент класса.
Большое семейство крючков useRef
Чем отличается этот четвертый способ использования ref?
useRef
Возвращает изменяемый объект ref, чей.current
Свойства инициализируются переданным параметром (initialValue).Возвращенный объект ref остается неизменным в течение всего времени жизни компонента.. А useRef может удобно хранить любое изменяемое значение, аналогично тому, как поля экземпляра используются в классе.
Именно благодаря этим характеристикамuseRef
а такжеcreateRef
Существует большая разница.
Вы можете запустить следующий код:
import React, { useState, useRef, useEffect } from "react";
export default function App() {
const [count, setCount] = useState(0);
const latestCount = useRef(count);
useEffect(() => {
latestCount.current = count;
});
function handleAlertclick() {
setTimeout(() => {
alert("latestCount.current:" + latestCount.current + '.. count: ' + count);
}, 2000);
}
return (
<div>
<p>当前count: {count} </p>
<button onClick={() => setCount(count + 1)}>count + 1</button>
<button onClick={handleAlertclick}> 提示 </button>
</div>
)
}
Затем выполните следующие действия:
- Нажмите 5 раз подряд
count + 1
кнопка - нажмите
提示
кнопка - нажмите "Готово"
提示
2 последовательных нажатия в течение 2 секунд после нажатия кнопкиcount + 1
кнопка - Дождитесь появления всплывающего окна с предупреждением.
Затем вы получите интересный ответ: всплывающее окно с предупреждением предложит:latestCount.current:7.. count: 5
. использоватьuseRef
может получить последнее значение, ноuseState
Но не могу.
По конкретным причинам, пожалуйста, обратитесь к личному блогу Дэна, одного из авторов реакции. или просмотретьРазница между функциональными компонентами React и компонентами классов заключается не только в состоянии и производительности!
Так действительно ли useRef так полезен? не совсем. Проблем еще много из-за характеристик выше него.
Вы можете попробовать запустить следующий код илинажмите здесь, чтобы посмотреть
import React, { useRef, createRef, useState } from "react";
import ReactDOM from "react-dom";
function App() {
const [renderIndex, setRenderIndex] = useState(1);
const refFromUseRef = useRef();
const refFromCreateRef = createRef();
if (!refFromUseRef.current) {
// 赋值操作
refFromUseRef.current = renderIndex;
}
if (!refFromCreateRef.current) {
// 赋值操作
refFromCreateRef.current = renderIndex;
}
return (
<div className="App">
Current render index: {renderIndex}
<br />
在refFromUseRef.current中记住的第一个渲染索引:
{refFromUseRef.current}
<br />
在refFromCreateRef.current中未能成功记住第一个渲染索引:
{refFromCreateRef.current}
<br />
<button onClick={() => setRenderIndex(prev => prev + 1)}>
数值 + 1
</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
В приведенном выше случае кнопка нажата независимо от того, чтоrefFromUseRef.current
всегда будет1
,а такжеrenderIndex
а такжеrefFromCreateRef.current
Изменится с событием клика; неожиданно, верно?
Потому что: useRef не уведомляет вас об изменении содержимого объекта ref. изменять.current
Свойство не вызывает повторную визуализацию компонента. Если вы хотите запустить некоторый код, когда React привязывает или отвязывает ссылку узла DOM, вам нужно использоватьcallback ref
реализовать.
Обобщить:
- useRef может получить ссылку на DOM
- useRef может получить последнее значение
- Содержимое useRef изменяется и не будет уведомлено
Из-за некоторых вышеперечисленных проблем я сначала не хотел говорить об useRef как о методе манипулирования ref.
пересылка ссылок
Нужно ли предоставлять DOM Refs родительскому компоненту?
В редких случаях вы можете захотеть сослаться на дочерний узел DOM в родительском компоненте. Как правило, это не рекомендуется, поскольку нарушает инкапсуляцию компонента, но иногда его можно использовать для активации фокуса или измерения размера или положения дочерних узлов DOM.
Как выставить ссылку на родительский компонент?
Если вы используете Rect 16.3 или выше, мы рекомендуем использовать пересылку в этом случае. Переадресация позволяет компоненту разоблачить Ref Component Ref точно так же, как оказывает свой собственный Ref.
Что такое реффорвард?
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
Как переслать в более низкой версии?
Если вы используете React 16.2 или более раннюю версию или вам требуется больше гибкости, чем переадресация ссылок, вы можете использовать ref для прямой передачи в качестве свойства со специальным именем.
Например следующее:
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.inputElement = React.createRef();
}
render() {
return (
<CustomTextInput inputRef={this.inputElement} />
);
}
}
Вот пошаговое объяснение того, что происходит в приведенном выше примере:
- Мы создаем ссылку React, вызывая React.createRef и назначая ее переменной ref.
- Указав REF как свойства JSX, мы передаем его вниз
<FancyButton ref={ref}>
. - React передает ref внутренней функции forwardRef (props, ref) => ... в качестве второго аргумента.
- Мы передаем параметр ref вниз
<button ref={ref}>
, указав его как свойство JSX. - Когда ref смонтирован, ref.current будет указывать на
<button>
узел ДОМ.
наконец
Добро пожаловать в публичный аккаунтПродвинутый курс по интерфейсуСерьезно изучите интерфейс и продвигайтесь вместе. Отвечать全栈
илиVue
Есть что подарить.