我无法理解为什么需要以下useImperialiveHandle
、useLayoutEffect
、和useDebugValue
挂钩,请给出它们可以使用的示例,但不要给出文档中的示例。
请允许我在回答这个问题之前说,所有这些挂钩很少使用。99%的时候,你不需要这些。它们只是为了涵盖一些罕见的极端情况。
通常,当您使用useRef
时,您会得到ref
所附加组件的实例值。这允许您直接与DOM元素交互。
useImperialiveHandle
非常相似,但它允许您做两件事:
模糊
,聚焦
,等等),从而允许对正常行为或完全不同的行为产生副作用。不过,您可以随意调用该函数李>您可能有许多原因想要执行上述任一操作;您可能不想向父级公开本机属性,或者您可能想更改本机函数的行为。可能有很多原因。但是,很少使用useImperativeHandle
。
useImperativeHandle
自定义使用ref
时暴露给父组件的实例值
实例
在本例中,我们将从ref
中获得的值将只包含我们在useImperativeHandle
中声明的函数blur
。它不包含任何其他属性(我正在记录值来演示这一点)。函数本身也是“定制的”,其行为与您通常期望的不同。在这里,它设置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.log(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>
useImperialiveHandle
允许您确定哪些属性将在引用上公开。在下面的示例中,我们有一个按钮组件,我们希望在该引用上公开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.log(Object.keys(buttonRef.current)); // ['someExposedProperty']
console.log("click in index.tsx");
buttonRef.current.someExposedProperty();
};
return (
<div>
<Button onClick={handleClick} ref={buttonRef} />
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
[Button.tsx]
import React, { useRef, useImperativeHandle, forwardRef } from "react";
function Button(props, ref) {
const buttonRef = useRef();
useImperativeHandle(ref, () => ({
someExposedProperty: () => {
console.log(`we're inside the exposed property function!`);
}
}));
return (
<button ref={buttonRef} {...props}>
Button
</button>
);
}
export default forwardRef(Button);
这里可用。
这与use效应
相同,但仅在所有DOM突变完成后才会触发。本文来自肯特C.多德和任何人一样解释了这两者的区别,他说:
99%的时候[use效应
]是你想要使用的。
我还没有看到任何例子能够很好地说明这一点,我也不确定我是否能够创造出任何东西。最好的说法是,只有当useffect
出现问题时,才应该使用useLayoutEffect
。
我觉得医生们做了一个很好的解释这个的例子。如果您有一个自定义钩子,并且您想在React DevTools中标记它,那么这就是您使用的。
如果你对此有任何具体的问题,那么最好是发表评论或问另一个问题,因为我觉得人们放在这里的任何东西都只是在重复文档,至少在我们找到更具体的问题之前是这样。
useImperialiveHandle
hook在我的一个用例中帮了我很多忙。
我创建了一个使用第三方库组件的网格组件。库本身有一个具有内置功能的大数据层,可以通过访问元素实例来使用该层。
然而,在我自己的网格组件中,我想用在网格上执行操作的方法来扩展它。此外,我还希望能够从网格组件外部执行这些方法。
这很容易通过在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 }} />
};