react ref转发
ref转发是一个非常实用的场景,父组件可以操作子组件的某些react 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.createRef创建了一个 React ref 并将其赋值给ref变量。 - 我们通过指定
ref为 JSX 属性,将其向下传递给<FancyButton ref={ref}>。 - React 传递
ref给fowardRef内函数(props, ref) => ...,作为其第二个参数。 - 我们向下转发该
ref参数到<button ref={ref}>,将其指定为 JSX 属性。 - 当 ref 挂载完成,
ref.current将指向<button>DOM 节点。注意 第二个参数
ref只在使用React.forwardRef定义组件时存在。常规函数和 class 组件不接收ref参数,且 props 中也不存在ref。 Ref 转发不仅限于 DOM 组件,你也可以转发 refs 到 class 组件实例中。
高阶组件的ref转发
function logProps(WrappedComponent) {class LogProps extends React.Component {componentDidUpdate(prevProps) {console.log('old props:', prevProps);console.log('new props:', this.props);}render() {return <WrappedComponent {...this.props} />;}}return LogProps;}class FancyButton extends React.Component {focus() {// ...}// ...}// 我们导出 LogProps,而不是 FancyButton。// 虽然它也会渲染一个 FancyButton。export default logProps(FancyButton);/*上面的示例有一点需要注意:refs 将不会透传下去。这是因为 ref 不是 prop 属性。就像 key 一样,其被 React 进行了特殊处理。如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。*/这意味着用于我们 FancyButton 组件的 refs 实际上将被挂载到 LogProps 组件:import FancyButton from './FancyButton';const ref = React.createRef();// 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。// 尽管渲染结果将是一样的,// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!// 这意味着我们不能调用例如 ref.current.focus() 这样的方法<FancyButtonlabel="Click Me"handleClick={handleClick}ref={ref}/>;
正确的使用方式
function logProps(Component) {class LogProps extends React.Component {componentDidUpdate(prevProps) {console.log('old props:', prevProps);console.log('new props:', this.props);}render() {const {forwardedRef, ...rest} = this.props;// 将自定义的 prop 属性 “forwardedRef” 定义为 refreturn <Component ref={forwardedRef} {...rest} />;}}// 注意 React.forwardRef 回调的第二个参数 “ref”。// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”// 然后它就可以被挂载到被 LogProps 包裹的子组件上。return React.forwardRef((props, ref) => {return <LogProps {...props} forwardedRef={ref} />;});}
定义别名,其实没声明卵用 在 DevTools 中显示自定义名称
React.forwardRef 接受一个渲染函数。React DevTools 使用该函数来决定为 ref 转发组件显示的内容。
例如,以下组件将在 DevTools 中显示为 “ForwardRef”:
const WrappedComponent = React.forwardRef((props, ref) => {return <LogProps {...props} forwardedRef={ref} />;});
如果你命名了渲染函数,DevTools 也将包含其名称(例如 “ForwardRef(myFunction)”):
const WrappedComponent = React.forwardRef(function myFunction(props, ref) {return <LogProps {...props} forwardedRef={ref} />;});
你甚至可以设置函数的 displayName 属性来包含被包裹组件的名称:
function logProps(Component) {class LogProps extends React.Component {// ...}function forwardRef(props, ref) {return <LogProps {...props} forwardedRef={ref} />;}// 在 DevTools 中为该组件提供一个更有用的显示名。// 例如 “ForwardRef(logProps(MyComponent))”const name = Component.displayName || Component.name;forwardRef.displayName = `logProps(${name})`;return React.forwardRef(forwardRef);}
