Search code examples
reactjstypescriptstyled-components

How to prevent a prop from being passed to the extended component?


My question is similar to this one, however I need to type a component. I have tried a multiple ways, but still getting errors.

I am not sure if the problem is in my understanding, or am I doing something wrong? Here is what I tried:

  • First approach works correctly but throws warning: React does not recognize the isActive prop on a DOM element
  • Second and Third throws TS error: No overload matches this call.
import * as React from "react";
import TextareaAutosize, { TextareaAutosizeProps } from "react-textarea-autosize";
import styled from "styled-components";

interface Props {
  isActive?: boolean;
}

interface ExtendedProps extends Props, TextareaAutosizeProps {}

const FirstTry = styled(TextareaAutosize)<Props>`
  color: ${({ isActive }) => (isActive ? "red" : "blue")};
`;

const SecondTry = styled(({ isActive, ...rest }: ExtendedProps) => (
  <TextareaAutosize {...rest} />
))`
  color: ${({ isActive }) => (isActive ? "red" : "blue")};
`;

const ThirdTry = ({ isActive, ...rest }: ExtendedProps) => {
  const Foo = styled(TextareaAutosize)<TextareaAutosizeProps>`
    color: ${isActive ? "red" : "blue"};
  `;

  return <Foo {...rest} />;
};

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <FirstTry isActive minRows={3} />
      {/* FirstTry - Warning: React does not recognize the `isActive` prop on a DOM element */}
      <SecondTry isActive minRows={3} />
      <ThirdTry isActive minRows={3} />
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

Link to sandbox: https://codesandbox.io/s/zen-cdn-lp8pl?file=/src/App.tsx


Solution

  • Your second approach looks good except for one small thing that causes the error: ref prop of TextareaAutosizeProps collides with ref prop of your styled component.

    ref (and key for that matter) is a tricky "prop" - you pass it to a component just like any other props yet it does not appear in your props (if you log them for example), it is handled differently.

    If you look at your second example:

    const SecondTry = styled(({ isActive, ...rest }: ExtendedProps) => (
      <TextareaAutosize {...rest} />
    ))`
      color: ${({ isActive }) => (isActive ? "red" : "blue")};
    `;
    

    You can see that you are not styling TextareaAutosize but the anonymous function component ({ isActive, ...rest }: ExtendedProps) => .... If you pass ref to your SecondTry component it will not appear in your props ({ isActive, ...rest }: ExtendedProps). Yet when extending TextareaAutosizeProps you are also saying that there will be such a prop and it will be of type HTMLTextAreaElement.

    So I can think of two solutions depending on your needs:

    If you don't need the ref prop on your SecondTry you can just omit it from your props:

    interface ExtendedProps extends Props, Omit<TextareaAutosizeProps, 'ref'> {}
    

    If you need it though you will need to use React.forwardRef function (see more about that here).