4

I'm attempting to import a React functionComponent from an SVG and then send that to another component as a prop to render that svg. With the setup below, this compiles fine, but eventually crashes when trying to render the svg in browser with:

Error: Objects are not valid as a React child (found: object with keys {$$typeof, render}). If you meant to render a collection of children, use an array instead.

Classes below are simplified. But the gist of what I'm trying to do is:

In overlay.tsx:

import { ReactComponent as icon } from "/icon.svg";
import CustomItem from "/customItem";

const Overlay: React.FC<OverlayProps> = () => {

    return (
        <div>
            <CustomItem icon={icon}/>
        </div>
    );
    export default Overlay;
}

and in customItem.tsx:

import React from "react";

export interface CustomItemProps {
    icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
}

const CustomItem: React.FC<CustomItemProps> = ({icon}) => {

    return (
        <div>
            {icon}
        </div>
    );
};

export default ApplicationsDropdownItem;

I assume my problem is somewhere around the syntax of {icon}, but I can not for the life of me find out what I'm suppose to use instead.

Excludos
  • 1,380
  • 1
  • 12
  • 24
  • importing the svg location as a string and painting it with does work, however that eliminates all css possibilities that comes with using svg as a component. – Excludos Apr 15 '20 at 13:55

3 Answers3

2

Answer

The icon you are importing is a component, therefore it must be called to render the JSX.

<Icon {...props}/> (correct) or {Icon(props)} (not recomended)

Since it is a component, you should also name it Icon and not icon.

Take a look at this blog post that explains SVGR.

TL;DR - Best approach for rendering components

A. Call the component in your render method with component syntax <MyComponent/> not MyComponent().

B. Instantiate your component as a variable, and pass that to your render method's JSX block.

More info

@DustInCompetent brought to light the issue of calling a component as a function inside a JSX block.

As explained here and here, that will lead to react not registering a components hooks and lead to state and other problems.

If you are implementing a High Level Component (HOC), then you should not call a component within the render method (return statement in functional components), as this leads to problems for similar registration issues of the component.

ryanjdillon
  • 17,658
  • 9
  • 85
  • 110
  • I read the `{fnComp(props)}` form is discouraged as it messes with event propagation and React dev tools or something – DustInComp Nov 22 '21 at 23:32
  • I also think that stylistically it is ugly and confusing, and I rarely see that used; though, it is possible. I'll update my example to clarify that. If you find/recall the resource that mentioned this, it would be great to add that here. – ryanjdillon Nov 24 '21 at 10:07
  • 1
    I think I found it: https://reactjs.org/docs/higher-order-components.html#dont-use-hocs-inside-the-render-method. It's about HOCs but the same applies to calling a function that returns a new component tree on every render. – DustInComp Dec 09 '21 at 08:38
  • That's very interesting. I understand that to mean that the component should rather be instantiated outside of the render/return statement of a component. Not necessarily that function syntax should not be used in a JSX block. But not sure. – ryanjdillon Dec 09 '21 at 09:40
  • Just googled and found this [SO answer](https://stackoverflow.com/q/67185279/943773), which led me to [this article](https://kentcdodds.com/blog/dont-call-a-react-function-component) that explains React cannot register a component's hooks when components are called as functions in JSX. – ryanjdillon Dec 09 '21 at 09:45
1
import React from "react";
import { ReactComponent as SampleIcon } from "/sample_icon.svg";

export interface CustomItemProps {
  Icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
}

const CustomItem: React.FC<CustomItemProps> = (props) => {
  const Temp = props.Icon as React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
  return (
    <div>
      <Temp/>
    </div>
  );
};

<CustomItem Icon={SampleIcon}/>
duril
  • 11
  • 1
  • 1
    Could you please elaborate your answer with some description so that it brings more context. Code-only answers are hard to read and understand. Thanks – Mario Petrovic Nov 22 '21 at 23:27
  • this answered my question of how to name/call the function component: const Temp = props.Icon as React.FunctionComponent>; ... – Dr. Hilarius Mar 08 '22 at 18:35
0

I think you should use <Icon /> instead of {icon} because it's a component.

  • That will not work. It does not understand where to get said component from – Excludos Sep 24 '20 at 18:39
  • @Excludos Literally assign `const Icon = props.icon` and use it as a component. It's already of type FunctionComponent, you just need to use it as such. – DustInComp Nov 22 '21 at 23:30