React.forwardRef
因为组件标签上的ref是直接绑定到组件最外层上,无法通过props再传递到组件的内部内容上
forwardRef使得父组件能够操作子组件内部的ref
1、子组件
const 子组件 = React.forwardRef((props, ref) => {
return(
<div>
<input ref={ref} type="text" />
<button>{props.children}</button>
</div>
)
});
2、父组件
将父组件的ref绑定到子组件上就可操控子组件,ref.current.xxx
function App() {
const ref = useRef();
return (
<div>
<子组件 ref={ref}>Click Me</子组件>
</div>
)
}
3、高阶组件中的应用
function logProps(Component) {
class SuffLogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
然后就可以被挂载到被 SuffLogProps 包裹的子组件上。
return <Component ref={forwardedRef} {...rest} />;
}
}
将自定义的prop属性 “forwardedRef” 定义为ref
return React.forwardRef((props, ref) => {
return <SuffLogProps {...props} forwardedRef={ref} />;
});
}
4、在React Devtools中自定义forwardedRef转发组件显示的内容
const WrappedComponent = React.forwardRef((props, ref) => { 显示为 “ForwardRef”
return <LogProps {...props} forwardedRef={ref} />;
});
const WrappedComponent = React.forwardRef( 显示为“ForwardRef(myFunction)”
function myFunction(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
);
function logProps(Component) { 显示为“ForwardRef(logProps(MyComponent))”
class LogProps extends React.Component {
// ...
}
function forwardRef(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
const name = Component.displayName || Component.name;
forwardRef.displayName = `logProps(${name})`;
return React.forwardRef(forwardRef);
}
ImperativeHandleHook
当使用React.forwardRef时,父组件的ref和子组件的ref都是同一个ref
当使用ImperativeHandleHook,可以使得父组件和子组件拥有各自的ref,并且子组件可选择性地暴露其他内容
1、子组件
const 子组件= React.forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () =>{
return{ 暴露给父组件的ref的值
focus: () => {
inputRef.current.focus();
},
其他内容...
}
});
return <input ref={inputRef} type="text" />
});
2、父组件
将父组件的ref绑定到子组件上,就可获取子组件通过ref暴露的内容,父组件Ref.current.子组件暴露的内容
const App = props => {
const 父组件Ref = useRef();
return (
<div>
<子组件 ref={fancyInputRef} />
<button
onClick={() => fancyInputRef.current.focus()}
>父组件调用子组件的 focus</button>
</div>
)
}
代码示例:
React.forwardRef:
import React, { useCallback, useRef } from 'react';
import ReactDOM from 'react-dom';
// 实现 ref 的转发
const FancyButton = React.forwardRef((props, ref) => (
<div>
<input ref={ref} type="text" />
<button>{props.children}</button>
</div>
));
// 父组件中使用子组件的 ref
function App() {
const ref = useRef();
const handleClick = useCallback(() => ref.current.focus(), [ ref ]);
return (
<div>
<FancyButton ref={ref}>Click Me</FancyButton>
<button onClick={handleClick}>获取焦点</button>
</div>
)
}
ReactDOM.render(<App />, root);
ImperativeHandleHook:
import React, { useRef, useImperativeHandle } from 'react';
import ReactDOM from 'react-dom';
const FancyInput = React.forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} type="text" />
});
const App = props => {
const fancyInputRef = useRef();
return (
<div>
<FancyInput ref={fancyInputRef} />
<button
onClick={() => fancyInputRef.current.focus()}
>父组件调用子组件的 focus</button>
</div>
)
}
ReactDOM.render(<App />, root);