forwardRef

forwardRef 允许你的组件使用 引用 向父组件公开 DOM 节点

¥forwardRef lets your component expose a DOM node to parent component with a ref.

const SomeComponent = forwardRef(render)

参考

¥Reference

forwardRef(render)

调用 forwardRef() 让你的组件接收引用并将其转发给子组件:

¥Call forwardRef() to let your component receive a ref and forward it to a child component:

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});

请参阅下面的更多示例。

¥See more examples below.

参数

¥Parameters

  • render:组件的渲染函数。React 使用你的组件从其父级接收到的属性 和 ref 调用此函数。你返回的 JSX 将是你组件的输出。

    ¥render: The render function for your component. React calls this function with the props and ref that your component received from its parent. The JSX you return will be the output of your component.

返回

¥Returns

forwardRef 返回一个可以在 JSX 中渲染的 React 组件。与定义为普通函数的 React 组件不同,forwardRef 返回的组件也能够接收 ref 属性。

¥forwardRef returns a React component that you can render in JSX. Unlike React components defined as plain functions, a component returned by forwardRef is also able to receive a ref prop.

注意事项

¥Caveats

  • 在严格模式下,React 将调用你的渲染函数两次,以便 帮助你发现意外杂质 这是仅开发行为,不会影响生产。如果你的渲染函数是纯函数(它应该是),这应该不会影响你组件的逻辑。其中一个调用的结果将被忽略。

    ¥In Strict Mode, React will call your render function twice in order to help you find accidental impurities. This is development-only behavior and does not affect production. If your render function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored.


render 函数

¥render function

forwardRef 接受渲染函数作为参数。React 使用 propsref 调用这个函数:

¥forwardRef accepts a render function as an argument. React calls this function with props and ref:

const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});

参数

¥Parameters

  • props:父组件传递的属性。

    ¥props: The props passed by the parent component.

  • ref:父组件传递的 ref 属性。ref 可以是对象或函数。如果父组件没有传递引用,则为 null。你应该将收到的 ref 传递给另一个组件,或者将其传递给 useImperativeHandle

    ¥ref: The ref attribute passed by the parent component. The ref can be an object or a function. If the parent component has not passed a ref, it will be null. You should either pass the ref you receive to another component, or pass it to useImperativeHandle.

返回

¥Returns

forwardRef 返回一个可以在 JSX 中渲染的 React 组件。与定义为普通函数的 React 组件不同,forwardRef 返回的组件能够采用 ref 属性。

¥forwardRef returns a React component that you can render in JSX. Unlike React components defined as plain functions, the component returned by forwardRef is able to take a ref prop.


用法

¥Usage

将 DOM 节点暴露给父组件

¥Exposing a DOM node to the parent component

默认情况下,每个组件的 DOM 节点都是私有的。但是,有时将 DOM 节点公开给父节点很有用 - 例如,允许对其进行聚焦。要选择加入,请将你的组件定义封装到 forwardRef() 中:

¥By default, each component’s DOM nodes are private. However, sometimes it’s useful to expose a DOM node to the parent—for example, to allow focusing it. To opt in, wrap your component definition into forwardRef():

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});

你将收到 ref 作为属性之后的第二个参数。将它传递给要公开的 DOM 节点:

¥You will receive a ref as the second argument after props. Pass it to the DOM node that you want to expose:

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});

这允许父 Form 组件访问 MyInput 公开的 <input> DOM 节点

¥This lets the parent Form component access the <input> DOM node exposed by MyInput:

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

Form 组件 传递引用MyInputMyInput 组件将该引用转发给 <input> 浏览器标签。结果,Form 组件可以访问 <input> DOM 节点并在其上调用 focus()

¥This Form component passes a ref to MyInput. The MyInput component forwards that ref to the <input> browser tag. As a result, the Form component can access that <input> DOM node and call focus() on it.

请记住,将引用公开到组件内的 DOM 节点会使以后更改组件的内部结构变得更加困难。你通常会从可重用的底层组件(如按钮或文本输入)公开 DOM 节点,但不会为应用级组件(如头像或评论)公开 DOM 节点。

¥Keep in mind that exposing a ref to the DOM node inside your component makes it harder to change your component’s internals later. You will typically expose DOM nodes from reusable low-level components like buttons or text inputs, but you won’t do it for application-level components like an avatar or a comment.

Examples of forwarding a ref

例子 1 / 2:
聚焦文本输入

¥Focusing a text input

单击按钮将聚焦输入。Form 组件定义了一个引用并将其传递给 MyInput 组件。MyInput 组件将该引用转发给浏览器 <input>。这让 Form 组件聚焦于 <input>

¥Clicking the button will focus the input. The Form component defines a ref and passes it to the MyInput component. The MyInput component forwards that ref to the browser <input>. This lets the Form component focus the <input>.

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


通过多个组件转发引用

¥Forwarding a ref through multiple components

除了将 ref 转发到 DOM 节点之外,你还可以将其转发到你自己的组件,例如 MyInput

¥Instead of forwarding a ref to a DOM node, you can forward it to your own component like MyInput:

const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});

如果该 MyInput 组件将引用转发给它的 <input>,则对 FormField 的引用将为你提供 <input>

¥If that MyInput component forwards a ref to its <input>, a ref to FormField will give you that <input>:

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<FormField label="Enter your name:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

Form 组件定义了一个引用并将其传递给 FormFieldFormField 组件将该引用转发给 MyInputMyInput 将其转发给浏览器 <input> DOM 节点。这就是 Form 访问该 DOM 节点的方式。

¥The Form component defines a ref and passes it to FormField. The FormField component forwards that ref to MyInput, which forwards it to a browser <input> DOM node. This is how Form accesses that DOM node.

import { useRef } from 'react';
import FormField from './FormField.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <FormField label="Enter your name:" ref={ref} isRequired={true} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


公开命令句柄而不是 DOM 节点

¥Exposing an imperative handle instead of a DOM node

无需公开整个 DOM 节点,你可以使用一组更受限制的方法公开一个自定义对象,称为命令式句柄。为此,你需要定义一个单独的引用来保存 DOM 节点:

¥Instead of exposing an entire DOM node, you can expose a custom object, called an imperative handle, with a more constrained set of methods. To do this, you’d need to define a separate ref to hold the DOM node:

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

// ...

return <input {...props} ref={inputRef} />;
});

将收到的 ref 传递给 useImperativeHandle,并指定要公开给 ref 的值:

¥Pass the ref you received to useImperativeHandle and specify the value you want to expose to the ref:

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);

return <input {...props} ref={inputRef} />;
});

如果某个组件获得了对 MyInput 的引用,它只会接收你的 { focus, scrollIntoView } 对象而不是 DOM 节点。这使你可以将公开的有关 DOM 节点的信息限制在最低限度。

¥If some component gets a ref to MyInput, it will only receive your { focus, scrollIntoView } object instead of the DOM node. This lets you limit the information you expose about your DOM node to the minimum.

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
    // This won't work because the DOM node isn't exposed:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput placeholder="Enter your name" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

阅读有关使用命令句柄的更多信息。

¥Read more about using imperative handles.

易犯错误

不要过度使用参考文献。你应该只将引用用于你不能表达为属性的命令式行为:例如,滚动到一个节点、聚焦一个节点、触发动画、选择文本等等。

¥Do not overuse refs. You should only use refs for imperative behaviors that you can’t express as props: for example, scrolling to a node, focusing a node, triggering an animation, selecting text, and so on.

如果你可以用属性来表达某些东西,你不应该使用引用。例如,与其从 Modal 组件中暴露 { open, close } 这样的命令式句柄,不如像 <Modal isOpen={isOpen} /> 那样将 isOpen 作为属性。副作用 可以帮助你通过属性暴露命令式行为。

¥If you can express something as a prop, you should not use a ref. For example, instead of exposing an imperative handle like { open, close } from a Modal component, it is better to take isOpen as a prop like <Modal isOpen={isOpen} />. Effects can help you expose imperative behaviors via props.


故障排除

¥Troubleshooting

我的组件封装在 forwardRef 中,但它的 ref 始终是 null

¥My component is wrapped in forwardRef, but the ref to it is always null

这通常意味着你忘记实际使用你收到的 ref

¥This usually means that you forgot to actually use the ref that you received.

例如,此组件不对其 ref 执行任何操作:

¥For example, this component doesn’t do anything with its ref:

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});

要修复它,请将 ref 传递给 DOM 节点或另一个可以接受引用的组件:

¥To fix it, pass the ref down to a DOM node or another component that can accept a ref:

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});

如果某些逻辑是有条件的,则 refMyInput 也可以是 null

¥The ref to MyInput could also be null if some of the logic is conditional:

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});

如果 showInputfalse,则引用不会被转发到任何节点,并且 MyInput 的引用将保持为空。如果条件隐藏在另一个组件中,例如本例中的 Panel,这就特别容易被忽略:

¥If showInput is false, then the ref won’t be forwarded to any node, and a ref to MyInput will remain empty. This is particularly easy to miss if the condition is hidden inside another component, like Panel in this example:

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});

React 中文网 - 粤ICP备13048890号