Я не могу понять, зачем нужны следующие хуки useImperativeHandle, useLayoutEffect и useDebugValue, можете привести примеры, когда их можно использовать, но не примеры из документации, пожалуйста.





useImperativeHandleuseImperativeHandle позволяет определить, какие свойства будут отображаться в ссылке. В приведенном ниже примере у нас есть компонент кнопки, и мы хотели бы предоставить свойство someExposedProperty для этой ссылки:
[index.tsx]
import React, { useRef } from "react";
import { render } from "react-dom";
import Button from "./Button";
import "./styles.css";
function App() {
const buttonRef = useRef(null);
const handleClick = () => {
console.info(Object.keys(buttonRef.current)); // ['someExposedProperty']
console.info("click in index.tsx");
buttonRef.current.someExposedProperty();
};
return (
<div>
<Button onClick = {handleClick} ref = {buttonRef} />
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
[Кнопка.tsx]
import React, { useRef, useImperativeHandle, forwardRef } from "react";
function Button(props, ref) {
const buttonRef = useRef();
useImperativeHandle(ref, () => ({
someExposedProperty: () => {
console.info(`we're inside the exposed property function!`);
}
}));
return (
<button ref = {buttonRef} {...props}>
Button
</button>
);
}
export default forwardRef(Button);
useLayoutEffectЭто то же самое, что и useEffect, но срабатывает только после завершения всех мутаций DOM. эта статья От Кента С. Доддса, как никто другой, объясняет разницу в отношении этих двух, он говорит:
99% of the time [
useEffect] is what you want to use.
Я не видел примеров, которые бы особенно хорошо это иллюстрировали, и я не уверен, что смогу что-то создать. Вероятно, лучше сказать, что вы должны использовать useLayoutEffect только тогда, когда у useEffect есть проблемы.
useDebugValueЯ чувствую, что документы делает довольно хороший пример объяснения этого. Если у вас есть пользовательский хук, и вы хотите пометить его в React DevTools, то используйте его.
Если у вас есть какие-либо конкретные проблемы с этим, то, вероятно, было бы лучше либо прокомментировать, либо задать другой вопрос, потому что я чувствую, что все, что люди помещают сюда, будет просто повторять документы, по крайней мере, до тех пор, пока мы не достигнем более конкретной проблемы.
@sawtaytoes, когда я построил его в песочнице кода, он действительно работал, в любом случае принятый ответ кажется гораздо лучше, чем этот.
useLayoutEffect когда вам нужно мутировать (изменить) DOM, сгенерированный React, полезно для интеграции библиотек, которые мутируют DOM с React.
В примере useImperativeHandle просто отсутствует правильный код в Table.jsx, я просто скопировал предоставленный вами фрагмент, и он работает. Спасибо!
это очень плохой пример для императивного дескриптора, потому что вы можете просто передать ref без него компоненту, и ничего не изменится
@AuthorProxy Я обновил ответ на версию кода, которая теперь работает. Надеюсь, вы сможете немного пересмотреть свой комментарий — другие сочли предыдущий пример полезным, и я считаю полезным попытаться оставить комментарии строго конструктивными.
Позвольте мне предварить этот ответ, заявив, что все эти хуки используются очень редко. В 99% случаев они вам не понадобятся. Они предназначены только для покрытия некоторых редких сценариев крайних случаев.
useImperativeHandleОбычно, когда вы используете useRef, вам дается значение экземпляра компонента, к которому прикреплен ref. Это позволяет вам напрямую взаимодействовать с элементом DOM.
useImperativeHandle очень похож, но позволяет делать две вещи:
blur, focus и т. д.) вашими собственными функциями, тем самым допуская побочные эффекты нормального поведения или вообще другое поведение. Хотя вы можете называть функцию как угодно.Может быть много причин, по которым вы хотите сделать что-либо из вышеперечисленного; вы можете не захотеть предоставлять собственные свойства родителю или, возможно, вы хотите изменить поведение собственной функции. Причин может быть много. Однако useImperativeHandle используется редко.
useImperativeHandlecustomizes the instance value that is exposed to parent components when usingref
Пример
В этом примере значение, которое мы получим из ref, будет содержать только функцию blur, которую мы объявили в нашем useImperativeHandle. Он не будет содержать никаких других свойств (Я регистрирую значение, чтобы продемонстрировать это). Сама функция также «настроена» так, чтобы вести себя не так, как вы обычно ожидаете. Здесь он устанавливает document.title и размывает ввод при вызове blur.
const MyInput = React.forwardRef((props, ref) => {
const [val, setVal] = React.useState('');
const inputRef = React.useRef();
React.useImperativeHandle(ref, () => ({
blur: () => {
document.title = val;
inputRef.current.blur();
}
}));
return (
<input
ref = {inputRef}
val = {val}
onChange = {e => setVal(e.target.value)}
{...props}
/>
);
});
const App = () => {
const ref = React.useRef(null);
const onBlur = () => {
console.info(ref.current); // Only contains one property!
ref.current.blur();
};
return <MyInput ref = {ref} onBlur = {onBlur} />;
};
ReactDOM.render(<App />, document.getElementById("app"));<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id = "app"></div>useLayoutEffectХотя в некоторой степени он похож на useEffect(), он отличается тем, что запускается после того, как React зафиксирует обновления в DOM. Используется в редких случаях, когда нужно рассчитать расстояние между элементами после обновления или сделать другие пост-апдейтные расчеты/побочные эффекты.
The signature is identical to
useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled insideuseLayoutEffectwill be flushed synchronously, before the browser has a chance to paint.
Пример
Предположим, у вас есть абсолютно позиционированный элемент, высота которого может варьироваться, и вы хотите расположить под ним еще один div. Вы можете использовать getBoundingCLientRect() для вычисления высоты родителя и верхних свойств, а затем просто применить их к верхнему свойству дочернего элемента.
Здесь вы хотели бы использовать useLayoutEffect, а не useEffect. Посмотрите, почему в примерах ниже:
С useEffect: (обратите внимание на нервное поведение)
const Message = ({boxRef, children}) => {
const msgRef = React.useRef(null);
React.useEffect(() => {
const rect = boxRef.current.getBoundingClientRect();
msgRef.current.style.top = `${rect.height + rect.top}px`;
}, []);
return <span ref = {msgRef} className = "msg">{children}</span>;
};
const App = () => {
const [show, setShow] = React.useState(false);
const boxRef = React.useRef(null);
return (
<div>
<div ref = {boxRef} className = "box" onClick = {() => setShow(prev => !prev)}>Click me</div>
{show && <Message boxRef = {boxRef}>Foo bar baz</Message>}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));.box {
position: absolute;
width: 100px;
height: 100px;
background: green;
color: white;
}
.msg {
position: relative;
border: 1px solid red;
}<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id = "app"></div>С useLayoutEffect:
const Message = ({boxRef, children}) => {
const msgRef = React.useRef(null);
React.useLayoutEffect(() => {
const rect = boxRef.current.getBoundingClientRect();
msgRef.current.style.top = `${rect.height + rect.top}px`;
}, []);
return <span ref = {msgRef} className = "msg">{children}</span>;
};
const App = () => {
const [show, setShow] = React.useState(false);
const boxRef = React.useRef(null);
return (
<div>
<div ref = {boxRef} className = "box" onClick = {() => setShow(prev => !prev)}>Click me</div>
{show && <Message boxRef = {boxRef}>Foo bar baz</Message>}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));.box {
position: absolute;
width: 100px;
height: 100px;
background: green;
color: white;
}
.msg {
position: relative;
border: 1px solid red;
}<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id = "app"></div>useDebugValueИногда вам может понадобиться отладить определенные значения или свойства, но для этого могут потребоваться дорогостоящие операции, которые могут повлиять на производительность.
useDebugValue вызывается только тогда, когда React DevTools открыт и соответствующий хук проверяется, что предотвращает какое-либо влияние на производительность.
useDebugValuecan be used to display a label for custom hooks in React DevTools.
Я лично никогда не использовал этот крючок, хотя. Может быть, кто-то в комментариях может дать некоторое представление с хорошим примером.
просто вопрос, если я разрабатываю повторно используемый компонент, он содержит какой-то дочерний компонент, и я хочу предоставить API, который может взаимодействовать с дочерним компонентом: например, функция поиска в компоненте сетки и выделение результата поиска. это хорошая причина использовать useImperativeHandle
@user192344 user192344 недостаточно контекста, чтобы ответить. Не совсем понятно, что вам нужно. Я предлагаю вам опубликовать новый вопрос и объяснить вашу проблему :)
@user192344 user192344 Я думаю, что то, что вы описываете, больше похоже на работу типа Redux, где вы должны передать функцию отправки, возвращаемую из useReducer(). Если вы хотите предоставить набор функций для манипулирования состоянием (думайте об этом как об API состояния), хотя useImperativeHandle() может работать, вероятно, не рекомендуется использовать его таким образом.
useImperativeHandle
обычно ловушка выставляет метод и свойства вашего функционального компонента другому компоненту, помещая функциональный компонент внутрь forwardRef пример
const Sidebar=forwardRef((props,ref)=>{
const [visibility,setVisibility]=useState(null)
const opensideBar=()=>{
setVisibility(!visibility)
}
useImperativeHandle(ref,()=>({
opensideBar:()=>{
set()
}
}))
return(
<Fragment>
<button onClick = {opensideBar}>SHOW_SIDEBAR</button>
{visibility==true?(
<aside className = "sidebar">
<ul className = "list-group ">
<li className = " list-group-item">HOME</li>
<li className = " list-group-item">ABOUT</li>
<li className = " list-group-item">SERVICES</li>
<li className = " list-group-item">CONTACT</li>
<li className = " list-group-item">GALLERY</li>
</ul>
</aside>
):null}
</Fragment>
)
}
//using sidebar component
class Main extends Component{
myRef=createRef();
render(){
return(
<Fragment>
<button onClick = {()=>{
///hear we calling sidebar component
this.myRef?.current?.opensideBar()
}}>
Show Sidebar
</button>
<Sidebar ref = {this.myRef}/>
</Fragment>
)
}
}
Крюк useImperativeHandle очень помог мне в моем случае использования.
Я создал компонент сетки, который использует компонент сторонней библиотеки. Сама библиотека имеет слой больших данных со встроенной функциональностью, которую можно использовать, обращаясь к экземпляру элемента.
Однако в моем собственном компоненте сетки я хочу расширить его методами, которые выполняют действия в сетке. Кроме того, я также хочу иметь возможность выполнять эти методы за пределами моего компонента сетки.
Этого легко добиться, добавив методы внутри хука useImperativeHandle, и тогда они будут доступны его родителю.
Мой компонент сетки выглядит примерно так:
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import ThirdPartyGrid from 'some-library';
export default forwardRef((props, forwardedRef) => {
const gridRef = useRef(null);
useImperativeHandle(forwardedRef,
() => ({
storeExpandedRecords () {
// Some code
},
restoreExpandedRecords () {
// Some code
},
}));
return (
<div ref = {forwardedRef}>
<ThirdPartyGrid
ref = {gridRef}
{...props.config}
/>
</div>
);
});
И затем в моем родителе я могу выполнить эти методы:
import React, { useRef, useEffect } from 'react';
export default function Parent () {
const gridRef = useRef(null);
const storeRecords = () => {
gridRef.current.storeExpandedRecords();
};
useEffect(() => {
storeRecords();
}, []);
return <GridWrapper ref = {gridRef} config = {{ something: true }} />
};
Я вижу, что на этот вопрос уже есть ответы. Итак, я просто хочу поделиться своими статьями о useImperativeHandle, useEffect и useLayoutEffect.
Я написал хук для статьи aboutuseImperativeHandle на основе примера, вы можете посмотреть его здесь: useImperativeHandle по примерам
Я также написал статью о эффекты в React, в которой подробно объясняются различия хуков использованиеЭффект и использоватьLayoutEffect: Руководство для начинающих по эффектам в React
Ваш пример
useImperativeHandleне работает.