Как реализовать компонент `Button`, используя `React.forwardRef`, чтобы дать возможность выбора отображаемого элемента (например, кнопки или ссылки), в зависимости от переданных параметров, и связать это с ref, принимая разные типы ссылок (`React.Ref<HTMLButtonElement>` или `React.Ref<HTMLAnchorElement>`) в зависимости от переданных параметров?
Для использования компонента с предоставленным ref в React.forwardRef, нужно передать ref в дочерний компонент, используя атрибут `ref`, и передать этот ref внутреннему компоненту через `React.forwardRef`.
Пример реализации компонента `Button` с использованием `React.forwardRef`:
```jsx
import React from 'react';
const Button = React.forwardRef(({ href, children, ...rest }, ref) => {
const linkButton = href ? 'a' : 'button';
return React.createElement(
linkButton,
{
...rest,
ref,
href: href || null
},
children
);
});
export default Button;
```
В данном примере компонент `Button` принимает параметры `href`, `children` и `rest`, а также переданный ref. В зависимости от того, передан ли `href`, компонент создает элемент `a` (для ссылки) или `button` (для кнопки) и передает ему все параметры.
Использование компонента `Button` может выглядеть следующим образом:
```jsx
import React, { useRef } from 'react';
import Button from './Button';
const App = () => {
const buttonRef = useRef(null);
const handleClick = () => {
buttonRef.current.focus();
}
return (
<div>
<Button ref={buttonRef} onClick={handleClick}>Click me</Button>
<Button ref={buttonRef} href="https://example.com">Go to example.com</Button>
</div>
);
}
export default App;
```
В данном примере компонент `Button` позволяет использовать как кнопку с текстом "Click me", так и ссылку на "example.com". И в обоих случаях можно передавать и использовать ref, указывая на элемент `button` или `a` в зависимости от переданных параметров.
<pre><code class="javascript">type ButtonType = 'button' | 'a';
interface ButtonTypeProps extends Record<ButtonType, object> {
button: {};
a: { href: string };
}
type CommonProps = {
children: React.ReactNode;
variant?: 'plain' | 'tonal' | 'filled' | 'outlined';
justify?: 'start' | 'center' | 'end';
};
type Props<T extends ButtonType> = ButtonTypeProps[T] & CommonProps & ComponentPropsWithoutRef<T>;
type ButtonComponent<T extends ButtonType = ButtonType> = UnionToIntersection<
T extends T ? ForwardRefExoticComponent<Props<T> & RefAttributes<ComponentRef<T>>> : never
>;
const isLinkProps = (props: Props<ButtonType>): props is Props<'a'> => 'href' in props;
const Button = forwardRef(
<T extends ButtonType = ButtonType>(props: Props<T>, ref: ForwardedRef<ComponentRef<T>>) => {
if (isLinkProps(props)) {
return (
<a ref={ref as ForwardedRef<ComponentRef<'a'>>} href={props.href} className="button">
{props.children}
</a>
);
}
return (
<button ref={ref as ForwardedRef<ComponentRef<'button'>>} type="button" className="button">
{props.children}
</button>
);
},
) as ButtonComponent;
Button.displayName = 'Button';</code></pre> <br/> <br/> <a href="https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDGMA0cDeAoOOGATzCTgGEJwIA7JWmABSgjAGcB1YGACwgCuMZADNMBIqXJUa9RqPGESZOADFoAdxRQAJqICiADwgxgaGZDlYJy8qICCMGFGAAjIUnaK4Izdr1IYjZSan66SAFBAL4+rCBwAORQqBgJANx4eEiGkLCSKgCqtMB0ACoQAJKMSFDsSBgltAA8BQB8cAC8cAAUBXDZMAw67HB9APw92gDmAFyjAJSd7QBuEMA6cHP0yzWLA0Mj3dNzwLQiNXAVix0razoSExUSW0g7UBl4tnAAQkIwZSEugl3E46Ak4AAfRIodKZU6DKAidDkX6g2ilKQsNgjfa0YaIerQHRNVH-dFSbAQVwAK3qMHa+EIILJcxwUQyhBQrLgvGSIjm7GcpymcHZeCimS+MhAdCxHE6uAkaF4wAANjpkrQ5sh0DAAHQ6jAAOQgOiQHLgy20wBQjDGcwSYFVKFO4KhCTJKFVbsSIjVqoiPoSghgqtOgYt1IEguAImI9sSgu0MCDaAYCKDQ1hYs+ITl7CapX6hkGeJGpIBZHaXQr5LI+YA2qUALpwABklGoMto+fbndk6fz3D4IdEhdaHy+tYsdHTheLpfxtYxKhrf0rSGro2KZUq1VqdMaTQkRdx+KLE3UUC0ugMxlM5moljn+fHfYcThcIM8TRnVjHpStEBmxwNsNR4BOmRoHQgpwMA7AADKnAA1r2XTdGArAcHMr7LlIrTzHMmHYnBIyvgkMLVu0CS8oE4KnHAxEcB80G0LBtYKr417+KI3Tzme5brnW5BrmiK6bhhWHsDhUnjtgfJzFeN5mpEv5PrO8iBOOBFLIqhCxj08FIbQqFSZJ2LzIsjKEHAyQwAIUC0D0Eg2XATQoLZgQdDgfJwCgIxKf4ERjn+6ZjhRCRATEtEiN5THsHqMUxGgzrsOwRooCASAdAARMydA5a0Lmubg8V6sqaoagwEolW5AD0KBFa58wWhKEh2Q5TndMVTT5U5fLeb5-mhNx4SqaFmkiE0wJCZFrQxLYuV9TlcApf56WZdleVCYVxWEDgZUVeqmo1TZTR1X1TVwC14qLMN07qVYHy1nqOjwU6KDEBlWUKgktawkAA" rel="nofollow">https://www.typescriptlang.org/play?#code/JYWwDg9g...</a>