Search code examples
reactjstypescriptcss-animationsstyled-componentsreact-props

React + Typescript + Styled Components pass props to keyframes & conditioning


I'm trying to pass props to keyframe element & do some conditional animation. Here's my code:

import styled, { keyframes } from "styled-components";
import {
  PRIMARY,
  SECONDARY,
  DANGER,
  TEXT,
  OUTLINE,
} from "../../constans";

interface PropTypes {
  color?: string;
  variant?: string;
  size?: string;
  shadow?: boolean;
  disabled?: boolean;
  hoverAnimation?: boolean;
}

const StyledButton = styled.button<PropTypes>`
  // [... SOME CODE ...]
  animation: ${(props) => hoverAnimate(props)} 2s infinite;
`;

const hoverAnimate = (props: PropTypes) => keyframes`
        100%{
            color: ${() => {
              console.log(props);
              if (props.variant) {
                if (props.color) {
                  return `var(--color-${props.color})`;
                }
                return "#444";
              }
              return "#fff";
            }};

            background: ${() => {
              if (props.variant === TEXT || props.variant === OUTLINE)
                return "#f00";
              console.log("object");
              if (props.color === PRIMARY) return "var(--color-primary)";
              if (props.color === SECONDARY) return "var(--color-secondary)";
              if (props.color === DANGER) return "var(--color-danger)";
              return "var(--color-default)";
            }};
        }
`;

What am I doing wrong?

TS complains:

Argument of type '() => string' is not assignable to parameter of type 'SimpleInterpolation'. Type '() => string' is missing the following properties from type 'readonly SimpleInterpolation[]': concat, join, slice, indexOf, and 17 more.

And, console.log(props) prints:

object

It's like a string. I can't expand this object in dev tools (even if try destructure).


Solution

  • It appears like you are trying to use an IIFE (immediately invoked function expression) to return a "string" that would be used inside template strings.

    But you are getting TypeScript error:

    Argument of type '() => string' is not assignable to parameter of type 'SimpleInterpolation'.

    because your current code:

    const hoverAnimate = (props: PropTypes) => keyframes`
      100%{
        color: ${() => {
          if (props.variant) {
            if (props.color) {
              return `var(--color-${props.color})`;
            }
            return "#444";
          }
          return "#fff";
        }};
        ...
    

    doesn't properly call the IIFE.

    Here is how to fix it by calling (note the additional ()s) the IIFE:

    const hoverAnimate = (props: PropTypes) => keyframes`
      100%{
        color: ${(() => { // Here, at start
          if (props.variant) {
            if (props.color) {
              return `var(--color-${props.color})`;
            }
            return "#444";
          }
          return "#fff";
        })()}; // And, here
        ...
    

    You need to do the same for background as well.