Open binaryartifex opened 2 years ago
@binaryartifex find any workarounds for this? I find myself in a similar scenario
Any updates on this? Have tried multiple workarounds to no avail.
I also have this issue. this is the codesandbox to reproduce the bug
https://codesandbox.io/s/unruffled-lehmann-k51cnc?file=/pages/index.tsx
for now, the workaround is using {...(buttonProps as any)}
sorry folks, i couldn't figure it out for the life of me either. I end up resigning myself to having a child motion.div in the insert element here. particularly with react-aria the only way i know there's no unintended properties propogating somewhere they shouldn't is thinking ahead about what needs to be animated, and wrapping those in a for-purpose motion.div. sorry i can't be more help. be good to get some feedback from framer themselves. in the absence of react-aria you can't compose react and motion typings together. I havn't tested this yet, was just playin around in the IDE then but this seems to do the trick....
import { HTMLMotionProps, motion } from "framer-motion";
import { forwardRef } from "react";
type ButtonProps = HTMLMotionProps<"button">;
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
function Button(props, ref) {
return (
<motion.button {...props} ref={ref}>
{props.children}
</motion.button>
);
},
);
This is an issue for us as well, we are heavily relying on polymorphism and this is a deal breaker. Maybe there is some potential here to align type definitions with react handler definitions? Any additional information which framer-motion provides could go into a separate argument if necessary (for instance drag handlers provide additional information through info
argument.
Hey guys. I got the same type issue, I found that using mergeProps
with MotionProps
or HTMLMotionProps
might help 🤔
Example and CodeSandbox preview.
import type { ForwardedRef } from "react";
import type { AriaButtonProps } from "react-aria";
import type { MotionProps } from "framer-motion";
import { forwardRef, useImperativeHandle, useRef } from "react";
import { motion, useReducedMotion } from "framer-motion";
import { useButton, mergeProps } from "react-aria";
// safe-motion
const useSafeMotion = (motion: MotionProps): MotionProps => {
const reduced = useReducedMotion();
return reduced ? {} : motion;
};
// button
const ButtonComponent = (
props: ButtonProps,
ref: ForwardedRef<HTMLButtonElement>
) => {
const { children, className, ...rest } = props;
const innerRef = useRef<HTMLButtonElement>(null);
useImperativeHandle(ref, () => innerRef.current!);
const { buttonProps } = useButton({ ...rest, children }, innerRef);
const safeMotion = useSafeMotion({
initial: { scale: 1 },
whileTap: { scale: 0.95 },
whileHover: { scale: 1.05 }
});
return (
<motion.button
ref={innerRef}
className={className}
{...mergeProps(buttonProps, safeMotion)}
>
{children}
</motion.button>
);
};
export interface ButtonProps extends AriaButtonProps {
className?: string;
}
export const Button = forwardRef(ButtonComponent);
import type { ForwardedRef } from "react";
import type { AriaButtonProps } from "react-aria";
import type { HTMLMotionProps } from "framer-motion";
import { forwardRef, useImperativeHandle, useRef } from "react";
import { motion } from "framer-motion";
import { useButton, mergeProps } from "react-aria";
const ButtonComponent = (
props: ButtonProps,
ref: ForwardedRef<HTMLButtonElement>
) => {
const { children, className, ...rest } = props;
const innerRef = useRef<HTMLButtonElement>(null);
useImperativeHandle(ref, () => innerRef.current!);
const { buttonProps } = useButton({ ...rest, children }, innerRef);
return (
<motion.button
ref={innerRef}
className={className}
// ⚠ note non-dom attributes that cannot be handled by framer-motion
{...mergeProps(rest, buttonProps)}
>
{children}
</motion.button>
);
};
export interface ButtonProps
extends AriaButtonProps,
Omit<HTMLMotionProps<"button">, keyof AriaButtonProps> {
className?: string;
}
export const Button2 = forwardRef(ButtonComponent);
I was able to resolve this with:
{...(buttonProps as ButtonHTMLAttributes<HTMLButtonElement> & MotionProps)}
Probably could be better but at least TS stops complaining.
Here to +1 experiencing this edge case and needing a solid solution. Polymorphism w/ framer becomes untenable nearly instantly, given the current configuration.
Describe the bug
Attempting to combine the efforts of framer-motion and react-aria into a custom design system while leveraging typescript. React aria returns a set of component props that are then spread onto the base component (a button in this instance) however theres substantial difference between several of the spread properties that I have been unable to resolve. At first i thought this might be a react-aria issue however the returned button props from the aria hook are of the type
React.ButtonHTMLAttributes<HTMLButtonElement>
which are straight from the react typings ecosystem.https://codesandbox.io/s/blissful-banach-sdgczr?file=/src/button/button.tsx
Steps to reproduce
Steps to reproduce the behavior:
AriaButtonProps
that returns amotion.button
useObjectRef
helper hook from @react-aria/utils to derive new ref objectuseButton
hook from react-aria library with props and derived ref -> destructure to expose buttonProps objectReact.ButtonHTMLAttributes<HTMLButtonElement>
Expected behavior Expected to be able to seamlessly spread React typings onto motion components. The actual functionality seems to work just fine as demonstrated in this youtube video that does exactly what i am attempting to do, however javascript is used https://www.youtube.com/watch?v=ydZSNUbHl_8
Environment details
OS Windows 11
VS CODE Version: 1.71.2 (user setup) Electron: 19.0.12 Chromium: 102.0.5005.167 Node.js: 16.14.2 V8: 10.2.154.15-electron.0 Sandboxed: No
DEPENDENCIES @react-aria/utils: "^3.13.3" framer-motion: "^7.5.1" react: "18.2.0", react-aria: "^3.19.0", react-dom: "18.2.0", typescript: "4.8.4"