React Dev tools show my Component as Unknown - reactjs

I have the following simple component:
import React from 'react'
import '../css.scss'
export default (props) => {
let activeClass = props.out ? 'is-active' : ''
return (
<div className='hamburgerWrapper'>
<button className={'hamburger hamburger--htla ' + activeClass}>
<span />
</button>
</div>
)
}
When I look for it in the react dev tools, I see:
Is this because I need to extend React component? Do I need to create this as a variable and do so?

This happens when you export an anonymous function as your component. If you name the function (component) and then export it, it will show up in the React Dev Tools properly.
const MyComponent = (props) => {}
export default MyComponent;

I realise the original question has been correctly answered already, but I just wanted to note a very similar issue you may encounter if using React.memo() or similar function. Consider the following code:
const MyComponent = React.memo(props => <div>Hello</div>)
export default MyComponent
The component will still display as Anonymous in devtools and certain React error messages (e.g. prop-types validation).
Ensuring that the Component is defined before trying to memoise it resolves this issue, e.g:
const MyComponent = props => <div>Hello</div>
export default React.memo(MyComponent)

To add to Michael Jaspers answer, if you want to use a named import (instead of default export), you can do like so:
const MyComponent = props => <div />
export { MyComponent }
The component will show in React DevTools with the correct name.
Note: If you had exported the expression directly:
export const MyComponent = props => <div />
This would then show as Anonymous or Unknown in React DevTools

Related

Is import case insensitive in React?

I have been following a React course online. When the course talked about hooks in React, I saw this sample code:
import AlertContext from '../../context/alert/alertContext'
But when I went to alertContext.js module, I noticed the default exported module object is alertContext, the object name started with "a" instead of "A". The code is like export default alertContext.
Does it mean import is case insensitive?
When you have a default export on a custom component, you can import it with any name you like.
default export can have any name inside it and can be imported with any other name on other components.
But its important to import the custom component with UpperCase as React understands uppercase as a custom component and lower case as a native component.
Native Components => <div> , <p>, <input> etc
Custom Components => <AlertContext>
Example:
/*customComponent.js*/
const customComponent = () => <div> I am Custom </div>
export default customComponent;
/*App.js*/
import MyCustomComponent from './customComponent'
const App = () => <MyCustomComponent />
export default App;

React Functional Components, how to export more than one component per file

I need to export different components for the same page because they act just as configurations to render other components, I´m new to react so I struggling in finding the solution for this. Here is a codesandbox with a simple example of what I want to achieve: https://codesandbox.io/s/adoring-elion-fq4zt?file=/src/App.js
Thanks!
---- EDITED ----
Thanks for all the answers, thanks to that I realized I had a typo in the component naming, that's why my previous tries didn't work.
I leave here the updated codesandbox, since it might be useful for others:
To export more than one component in a module simply do the following:
export const ComponentOne = () => <span>Some content for ComponentOne</span>;
export const ComponentTwo = () => <span>Some content for ComponentOne</span>;
Then, when you need to import these components simply do:
import { ComponentOne, ComponentTwo } from "./path/of/components"
Please note that the import is with {} because the components are not exported as default.
On the other side, if you have only a component in your file you can simply do the following:
const Component = () => <span>Some content for Component</span>;
export default Component;
And than import as default, without the {} as in the following example:
import Component from "./path/of/components"
// component.js
export const someCom1 = () => {
return <div>Hello 1</div>
}
export const someCom2 = () => {
return <div>Hello 2</div>
}
// parent.js
import {someCom1, someCom2 } from './component.js'
in your page file remove "export" from the: export function componentOn
and keep at the button: export { ComponentOne, ComponentTwo };
to import it use: import {ComponentOne,ComponentTwo} from "./PageOne";
You can keep both components in a single wrapper like this
PageOne.js
import React from "react";
import ComponentOne from "./ComponentOne";
import ComponentTwo from "./ComponentTwo";
export default function PageOne() {
return (
<>
<ComponentOne title={"This is the title"} />;
<ComponentTwo description={"This is the description"} />;
</>
);
}
App.js
import React from "react";
import "./styles.css";
import PageOne from "./PageOne";
export default function App() {
return (
<div className="App">
<PageOne />
</div>
);
}

Sending a React.FunctionComponent<React.SVGProps<SVGSVGElement>> as a prop to another component

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.
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.
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}/>
I think you should use <Icon /> instead of {icon} because it's a component.

Gatsby: Accessing props array's length in a component leads to TypeError (property undefined)

Problem
In index.js I passed a modifier prop with the value of ["fullWidth", "secondMod"] to the imported Container sub-component:
index.js
<Container>
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
</Container>
// Modifier Prop comes here
<Container modifier={["fullWidth", "secondMod"]}>
<Img fluid={data.placeholderImage.childImageSharp.fluid} />
</Container>
Over in container.js, I want to access the array in the console.log:
container.js
import React from "react"
import PropTypes from 'prop-types';
// Styles
import container from "../styles/components/container.module.scss"
const Container = (props) => {
console.log(props.modifier.length); // TypeError: Cannot read property 'length' of undefined
console.log(props.modifier); // (2) ["fullWidth", "secondMod"]
console.log(props.children.length); // 3
return (
<section className={container.section}>
<div className={`${container.container} ${props.modifier}`}>
{props.children}
</div>
</section>
)
}
export default Container
Nonetheless, the first console.log(props.modifier.length) throws an error. The error's page tite is saying:
TypeError: Cannot read property 'length' of undefined
Same e.g. for using props.modifier[0].
Problem Source
Thanks to the comment of #PompolutZ, the problem for the error is the first <Container> in index.js. Since it does not pass a modifier prop, props.modifier and props.modifier.length are undefined.
Solution needed
Following the Problem Source, I don't know what is Best Practice for differentiating between sub-components (here <Container>) that pass a prop and those which don't. I still want to process the props, but don't want React to throw an error, if there is one that didn't pass a prop.
Using defaultProps can prevent React from throwing an Error. In this case that would mean to add the following code to container.js after the closing bracket (}) of const Container and before export default Container:
Container.defaultProps = {
modifier: [],
}
Also see the React Documentation on default Props for that.
I would have written it like this (container.js):
import React from "react"
import PropTypes from 'prop-types';
// Styles
import container from "../styles/components/container.module.scss"
export default ({ children, modifier = [] }) => {
console.log(modifier && modifier.length);
console.log(modifier && modifier);
console.log(children && children.length);
return (
<section>
<div>{children}</div>
</section>
);
};

TypeError: Cannot read property 'map' of undefined Reactjs

Newbie to React and I need help again - everything was fine untill i shifted the code from App.js to separate component - and b/c it is the stateless component, i am using props and map function to access the value and state from App.js but it is not happy -- help please
import React from 'react';
const Recipes = props => (
<div>
{props.recipes.map(recipe => (
<div key={recipe.recipe_id}>
<img src={recipe.image_url} alt={recipe.title} />
<p>{recipe.title}</p>
</div>
))}
</div>
);
export default Recipes;
This just means that you don't pass in recipes properly as a prop where you render <Recipes />. Either recipes is null or incorrectly formatted.
ex:
// App.js
import React from 'react';
const App = () => {
const recipes = [{
recipe_id: '<id>',
image_url: '<some url>',
title: 'Lé title'
}];
// recipes could be null/undefined or not even passed as a prop
return (
<Recipes recipes={recipes} />
);
}
export default App;
So it's hard to know exactly what's happening without being able to see how you are passing down props, and exactly what data they contain. The error you are getting implies that you aren't actually sending the recipes array correctly.
I honestly never use stateless functions in react anymore, because PureComponent generally preforms better because of it's built in shouldComponentUpdate which prevents unnecessary re-renders. So here's how I would write that component:
import React, { PureComponent } from 'react';
class Recipes extends PureComonent {
recipeList = () => {
const recipes = this.props;
const recipeArray = recipes.map((recipe) => {
<div key={recipe.recipe_id}>
<img src={recipe.image_url} alt={recipe.title} />
<p>{recipe.title}</p>
</div>
});
return recipeArray;
}
render () {
return () {
<div>
{this.recipeList()}
</div>
}
}
}
export default Recipes;
That being said, my guess about the way you wrote your component is that if you were to console out props you would find that it was actually equal to recipes, which is why recipes.recipes is undefined.

Resources