提问者:小点点

何时使用UseImperialiveHandle、useLayoutEffect和useDebugValue


我无法理解为什么需要以下useImperialiveHandleuseLayoutEffect、和useDebugValue挂钩,请给出它们可以使用的示例,但不要给出文档中的示例。


共3个答案

匿名用户

请允许我在回答这个问题之前说,所有这些挂钩很少使用。99%的时候,你不需要这些。它们只是为了涵盖一些罕见的极端情况。

通常,当您使用useRef时,您会得到ref所附加组件的实例值。这允许您直接与DOM元素交互。

useImperialiveHandle非常相似,但它允许您做两件事:

  1. 它使您可以控制返回的值。不返回实例元素,而是显式声明返回值(请参见下面的代码段)
  2. 它允许您用自己的函数替换本机函数(例如模糊聚焦,等等),从而允许对正常行为或完全不同的行为产生副作用。不过,您可以随意调用该函数

您可能有许多原因想要执行上述任一操作;您可能不想向父级公开本机属性,或者您可能想更改本机函数的行为。可能有很多原因。但是,很少使用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中标记它,那么这就是您使用的。

如果你对此有任何具体的问题,那么最好是发表评论或问另一个问题,因为我觉得人们放在这里的任何东西都只是在重复文档,至少在我们找到更具体的问题之前是这样。

匿名用户

useImperialiveHandlehook在我的一个用例中帮了我很多忙。

我创建了一个使用第三方库组件的网格组件。库本身有一个具有内置功能的大数据层,可以通过访问元素实例来使用该层。

然而,在我自己的网格组件中,我想用在网格上执行操作的方法来扩展它。此外,我还希望能够从网格组件外部执行这些方法。

这很容易通过在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 }} />
};