Rendering multiple child components via map function on data object - reactjs

I'm currently learning react. At this point I'm trying to render multiple child components within the parent compnent by calling the map method on the data object array that is passed to the parent.
My code looks like this:
index.tsx:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import {MOCK_CONTENTS} from "./DataDomain/Mocks/MockContents";
import SwipeView from "./components/SwipeView";
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<SwipeView contents={MOCK_CONTENTS}/>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more:
reportWebVitals();
SwipeView.tsx
import {Content} from "../DataDomain/Entities/Content";
import ContentCard from "./ContentCard";
interface SwipeViewProps {
contents: Content[];
}
function SwipeView({ contents }: SwipeViewProps): JSX.Element {
return <> {contents.map( content => {
ContentCard({content});
})} </>
}
export default SwipeView;
ContentCard.tsx
import React from "react";
import {Content} from "../DataDomain/Entities/Content";
interface ContentCardProps {
content: Content;
}
function ContentCard({ content }: ContentCardProps): JSX.Element {
return (
<>
<h1>{ content.title }</h1>
</>
);
}
export default ContentCard;
Where MOCK_CONTENTS is a array of the data objects.
The problem is that I'm just getting a white page and no error whatsoever.

this is the correct way.
SwipeView.tsx
import {Content} from "../DataDomain/Entities/Content";
import ContentCard from "./ContentCard";
interface SwipeViewProps {
contents: Content[];
}
function SwipeView({ contents }: SwipeViewProps): JSX.Element {
return <> {contents.map( (content,index) => <ContentCard key={index} content={content} /> )} </>
}
export default SwipeView;
note: it's better to use a specific id for the key instead of an index.

You're missing a return statement in your SwipeView.tsx component.
function SwipeView({ contents }: SwipeViewProps): JSX.Element {
return <>{contents.map((content, index) => {
<ContentCard key={index} content={content} />; // Missing return here
})} </>
}
With braces, you have to explicitly return your JSX. You can implicitly return JSX by instead wrapping your JSX in a parenthesis instead of braces.
For example:
function SwipeView({ contents }: SwipeViewProps): JSX.Element {
return <>{contents.map((content, index) => <ContentCard key={index} content={content} />)}</>;
}
In React, it is also vital that you always have a key prop when returning JSX within a loop so React is able to easily detect which component has been updated during it's reconciliation re-render process. Note the addition of key in each example.

Related

'X' cannot be used as a JSX component. Its return type 'Element[]' is not a valid JSX element

I am rending two simple joke cards in TypeScript and the cards do show up in my browser but I also get this error:
'Jokes' cannot be used as a JSX component.
Its return type 'Element[]' is not a valid JSX element.
Type 'Element[]' is missing the following properties from type 'ReactElement<any, any>': type, props, key
I am new to TypeScript and cannot figure out for the life of me what the problem is. I have tried adding <></> between the <Jokes /> in my App.tsx file but that doesn't work. I can't figure it out and was hoping someone could guide me in fixing this.
Jokes.tsx
import { jokeList, JokesPunchlines } from './jokeList';
import './App.css';
function Jokes() {
return (
jokeList.map((joking: JokesPunchlines) => {
return (
<div className="box">
<strong>{joking.setup}</strong>
<br />
<p>{joking.punchline}</p>
</div>
);
})
);
}
export default Jokes;
Joke List
export interface JokesPunchlines {
id: number,
setup: string,
punchline: string,
}
export const jokeList: JokesPunchlines[] = [
{
id: 1,
setup: "What's the best thing about a Boolean?",
punchline: "Even if you're wrong, you're only off by a bit"
},
{
id: 2,
setup: "Why do programmers wear glasses?",
punchline: "Because they need to C#"
}
];
App.tsx
import './App.css';
import Jokes from './Jokes';
function App() {
return (
<Jokes />
);
}
export default App;
index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
I have tried adding <></> between the <Jokes /> in my App.tsx file but that doesn't work. I can't figure it out and was hoping someone could guide me in fixing this.
Just as the error says, an array of React components isn't a React component itself. While it'll still work in the JavaScript, TypeScript doesn't like it. You can rectify it by adding a JSX fragment around the returned array, and interpolate the array into the fragment. You can also leave off the : JokesPunchlines since jokeList is typed properly (and maybe call the mapping argument, a joke object, joke or jokeObj or something - joking isn't a great variable name).
function Jokes() {
return (
<>
{
jokeList.map((joke) => {
return (
<div className="box">
<strong>{joke.setup}</strong>
<br />
<p>{joke.punchline}</p>
</div>
);
})
}
</>
);
}
By using implicite return typescript has some difficultie.
You can type function Jokes with return type. Its will help.
also this should do the job.
import { jokeList, JokesPunchlines } from './jokeList';
import './App.css';
function Jokes() {
return <>{
jokeList.map((joking: JokesPunchlines) => {
return (
<div className="box">
<strong>{joking.setup}</strong>
<br />
<p>{joking.punchline}</p>
</div>
);
})
}</>;
}
export default Jokes;

How to restrict props.children in react typescript?

import React from 'react';
import './App.css';
import Message from "./components/Message";
function App() {
return (
<div className="App">
<Message>
<p>Hello World</p>
</Message>
<Message>
<a>Hello World</a>
</Message>
<Message>
<button>Hello World</button>
</Message>
</div>
);
}
export default App;
import React, {FunctionComponent} from 'react';
interface OwnProps {
children: React.ReactElement<HTMLParagraphElement>;
}
type Props = OwnProps;
const Message: FunctionComponent<Props> = ({children}) => {
return (
<h1>
{children}
</h1>
);
};
export default Message;
The above code I can pass any HTMLElementTag as props.
Is there any way to restrict ReactElement to only specific element ?
For example only with p tag or button tag etc.
You can render specific children with React.Children by checking its type.
Below example only renders p tag elements.
import React, { Children } from "react";
interface Props {
children: React.ReactElement<HTMLParagraphElement | HTMLHeadElement>[] | React.ReactElement<HTMLParagraphElement | HTMLHeadElement>;
}
const Component: React.FC<Props> = ({ children }) => {
const elements = Children.map(children, (child) => {
if ((child as React.ReactElement).type === "p") {
return child;
}
return null;
})?.filter(Boolean);
return <div>{elements}</div>;
};
export default Component;
Note that type of custom component is a function, not string.
And if you want to check types of nested html elements, you need to recursively check types of react elements children.
I do not know what you are trying to do here, but there may be a better approach than restricting render of children elements.
I think this is the wrong way to be going about implementing HTMl here: if you want a <h1> element to render then you shouldn't be passing <p> or <button> elements into it.
Why not just leave the functional component input here as just a string? Like so:
import React from 'react';
interface MessageProps {
messageText: string;
}
const Message: React.FC<MessageProps> = ({ messageText }) => {
return (
<h1>
{messageText}
</h1>
);
};
export default Message;
//////
import React from 'react';
import Message from "./components/Message";
function App() {
return (
<div className="App">
<Message messageText="Hello world" />
</div>
);
}
export default App;
you can use following for static checking:
type Props = { children: React.ReactNode } //-- allow any React valid Node
// type Props = { children: never } //-- will not allow children to be passed

Map through two arrays of components and strings and render in one component

I have two arrays that I want to map through:
const social = ["Snapchat", "TikTok", "Dribbble", "Discord", "Facebook"];
const socialIcons = [<SnapchatIcon />, <DribbbleIcon />];
The socialIcons array are all components
How can I send both values as props into my DummyRectangle component? Here is my current code:
{social.map((s, index) => (
<div className="dummy_buttonsWrapper">
<DummRectangle social={s} socialIcons={i} />
</div>
))}
And here is DummyRectangle component:
function DummRectangle({ social, socialIcons }) {
// console.log("---->", socialIcons);
return (
<div>
<p>{social}</p>
{<socialIcon/>} // render social icon component
</div>
);
}
To do so, you don't need to wrap tags around your socialIcon in your DummRectangle. Also, it doesn't seem that you are passing the socialIcon component at all. If I were you, I would do something like this:
The following two are the components as an example that you would like to render (in your case - socialIcons)
// Comp1.js
import React from "react";
const Comp1 = () => <div>actual Comp1</div>;
export default Comp1;
// Comp2.js
import React from "react";
const Comp2 = () => <div>actual Comp2</div>;
export default Comp2;
Now, in your main Parent component, you would simply get the current component of the componentName (in your case - social) by accessing your component's array with an index. Then, you would pass this currentComponent as props to your Child component where you want to render it.
// App.js
import React from "react";
import Comp1 from "./Comp1";
import Comp2 from "./Comp2";
import DummyComponent from "./DummyComponent";
export default function App() {
const componentNames = ["Comp1", "Comp2"];
const components = [<Comp1 />, <Comp2 />];
return (
<div className="App">
{componentNames.map((name, index) => {
const currentComponent = components[index];
return (
<div>
<DummyComponent componentName={name} component={currentComponent} />
</div>
);
})}
</div>
);
}
In your Child component, you can simply render it by enclosing it into the brackets - no need to add tags. React will do all the rendering for you. In your case it would be { socialIcon }
// DummyComponent.js
import React from "react";
const DummyComponent = ({ componentName, component }) => {
return (
<div>
<p>{componentName}</p>
{component}
</div>
);
};
export default DummyComponent;
Link to Codesandbox with the above code for reference: click here

React Function to Generate random name from Array error

I'm getting an error with my component "TypeError: Cannot read property 'length' of undefined" from my RandomName component in my react app . What am I doing wrong?
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
function RandomName(props) {
const [name, newName] = useState(0);
const { names } = props;
return (
<div>
<h2>{name}</h2>
<button onClick={() => newName(names[Math.floor(Math.random()*names.length)])}>
New Name
</button>
</div>
);
}
ReactDOM.render(
<RandomName names={['Paul', 'David', 'Kevin']} />,
document.getElementById('root')
);
export default RandomName
You can generate an array first and then you use faker to put fake names in it.
const a = new Array(50).fill(null)
.map(e =>
e = faker.fake("{{name.lastName}}, {{name.firstName}}"))
Your code above seems to be correct but instead of 0 you can pass ''. But from Github repo I noticed you are not passing names in App.js.
<Switch>
<Route path="/">
<>
<RandomName names={['Paul', 'David', 'Kevin']} />
</>
</Route>
</Switch>
Updated Code
You have 3 ways
use typescript to check props data type
function RandomName({names=[]}:{names:Array<string>}){...}
use below code for check propTypes
import PropTypes from 'prop-types';
.
.
.
RandomName .propTypes = {
names: PropTypes.arrayOf(PropTypes.string)
}
and use below code to define function
function RandomName({names=[],...restprops}){...}
I was able to get the component working like so - thank you all for your help:
import React, { useState } from 'react';
import './index.css'
const names=['Mark', 'Steve', 'Tom']
function RandomName() {
const [name, newName] = useState('Bob');
return (
<div id="RandomName-container">
<h2>{name}</h2>
<button onClick={() => newName(names[Math.floor(Math.random()*names.length)])}>
New Name
</button>
</div>
);
}
export default RandomName

How to add {props.children} to a React component

i have many components which have {props.children} deeply nested inside.
considery DRY principle is there a way to add this using some React pattern.
example
let's say i have two components,
Comp1.js
import React from "react";
const Comp1 = props => {
return (
<div>
<h1>{props.children}</h1>
</div>
);
};
export default Comp1;
Comp2.js
import React from "react";
const Comp2 = props => {
return (
<div>
<div>
<h1>{props.children}</h1>
</div>
</div>
);
};
export default Comp2;
if you see above code we have both Comp1 and Comp2 have line of code {props.children} repeated inside.
what i want now is some function which will add this line of code, something like below,
const addPropsChildrenToComp = (Comp)=>{
return(
(props)=>{
///do somehting here
}
)
}
const Comp1 = props => {
return (
<div>
<h1></h1>
</div>
);
};
Comp1WithPropsChildren = addPropsChildrenToComp(Comp1)
using HOC doesn't work because, in HOC we never modify passed component.
anyway to aceve this.?
to get more idea of my problem see this demo: https://codesandbox.io/s/trusting-http-pd1yu
in there i woul like to see CompWithPropsChildren component render props.children inside it.
I think I see what you're trying to get to, and you can accomplish this just using another component.
import React from "react";
import ChildComp from "./ChildComp";
const Comp1 = props => {
return (
<div>
<ChildComp {...props} />
</div>
);
};
export default Comp1;
import React from "react";
const ChildComp = props => {
return <h1>{props.children}</h1>
}
Assuming your ChildComp has some complex logic you don't want to duplicate, this will make it reusable for you.

Resources