一、hook API
useAsync 和useAsyncRetry 都是 react-use的API,useState、useEffect、useRef、
- useAsync
const mgm = useAsync((): Promise<IMGMDetail|undefined> => getMGMActivity({ id }), [id])
- useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])
// ref:定义 current 对象的 ref
// createHandle:一个函数,返回值是一个对象,即这个 ref 的 current
// 对象 [deps]:即依赖列表,当监听的依赖发生变化,useImperativeHandle 才会重新将子组件的实例属性输出到父组件
useImperativeHandle 的第一个参数时ref,第二个是一个立即执行函数。
useImperativeHandle可以让你在使用 ref 时自定义暴露给父组件的实例值,与 forwardRef 一起使用:
渲染 <FancyInput ref={inputRef} /> 的父组件可以调用 inputRef.current.focus()。
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
useRef
const refContainer = useRef(initialValue);
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”。
// 命令式地访问子组件
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现。
二、Refs 转发
Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必需的。但其对某些组件,尤其是可重用的组件库是很有用的。
React.forwardRef 会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特别有用:
React.forwardRef 接受渲染函数作为参数。React 将使用 props 和 ref 作为参数来调用此函数,函数式组件默认不可以加 ref,它不像类组件那样有自己的实例。这个 API 一般是函数式组件用来接收父组件传来的 ref。
1. 转发 refs 到 DOM 组件
Ref 转发是一个可选特性,其允许某些组件接收 ref,并将其向下传递(换句话说,“转发”它)给子组件
在下面的示例中,FancyButton 使用 React.forwardRef 来获取传递给它的 ref,然后转发到它渲染的 DOM button:这样,使用 FancyButton 的组件可以获取底层 DOM 节点 button 的 ref ,并在必要时访问,就像其直接使用 DOM button 一样。
// 第二个参数 ref 只在使用 React.forwardRef 定义组件时存在
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>;
2. 在高阶组件中转发 refs
我们可以使用 React.forwardRef API 明确地将 refs 转发到内部的 FancyButton 组件。React.forwardRef 接受一个渲染函数,其接收 props 和 ref 参数并返回一个 React 节点。例如:
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” 定义为 ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}