英文:
dynamically generate react components from data from an object as props
问题
ProjectMenuItem 组件:
export function ProjectMenuItem({ projectName }: { projectName: string }) {
return (
<p>{projectName}</p>
);
}
父组件:
export function ProjectList() {
const projects = {
project1: {
title: 'Project 1',
},
project2: {
title: 'Project 2',
},
project3: {
title: 'Project 3',
},
};
function generateProjectMenuItems() {
const projectMenuItems = [];
for (const project in projects) {
const projectName: string = projects[project as keyof typeof projects].title;
projectMenuItems.push(<ProjectMenuItem projectName={projectName} />);
}
return projectMenuItems;
}
return (
<div className="project-list flexbox">
<p>project components go here</p>
{generateProjectMenuItems()}
</div>
)
}
您的代码在语法上需要一些修改,确保使用JSX语法。我已经更新了 ProjectMenuItem
组件和父组件的代码,使其正确使用JSX。希望这对您有所帮助。
英文:
Context: trying to build a simple todo list app in react and typescript for educational purposes.
What I'm trying to do: Render as many ProjectMenuItem components as there are individual projects in my projects
object, using the title of the project as props to pass into the components. "Project 1, Project 2, Project 3" should render to the screen as p elements.
What happens instead: The app does not compile and I get an error saying: Uncaught Error: Objects are not valid as a React child (found: object with keys {projectName}). If you meant to render a collection of children, use an array instead.
ProjectMenuItem component:
export function ProjectMenuItem(projectName: any){
return(
<p>{projectName}</p>
);
}
Parent Component:
export function ProjectList(){
const projects = {
project1: {
title: 'Project 1',
},
project2: {
title: 'Project 2',
},
project3: {
title: 'Project 3',
},
};
function generateProjectMenuItems() {
const projectMenuItems = [];
for (const project in projects) {
const projectName: string = projects[project as keyof typeof projects].title;
projectMenuItems.push(<ProjectMenuItem projectName={projectName} />);
}
return projectMenuItems;
}
return(
<div className="project-list flexbox">
<p>project components go here</p>
{generateProjectMenuItems()}
</div>
)
}
Have tried: I know it is saying I should use it as an array, so I tried mapping the projectMenuItems array to an array of span elements containing the components, but this results in the same error message.
return projectMenuItems.map(el => <span className="project-span" key={Math.random().toString()}>{el}</span>);
TL;DR I am trying to generate components for each object in a larger object and I'm not sure what I'm doing wrong. Very new to react.
答案1
得分: 3
你的 ProjectMenuItem
组件还不太对。你希望将 props 作为一个(解构的)对象来接受:
export function ProjectMenuItem({ projectName }: { projectName: string }) {
目前,React 会将整个 props 对象提供给它,它接收为 projectName
。当它尝试在 <p>{projectName}</p>
中渲染它时,会给你报错 "Objects are not valid"。
英文:
Your ProjectMenuItem
component isn't quite right. You want to accept the props as a (destructured) object:
export function ProjectMenuItem({ projectName }: { projectName: string }) {
As it is, React is providing it with the entire props object, which it receives as projectName
. When it tries to render this in <p>{projectName}</p>
it is giving you the error "Objects are not valid".
答案2
得分: 0
以下是您要翻译的内容:
Parent Component:
import React, {useContext} from "react";
import { AllContext } from "../App";
import ProjectMenuItem from "./ProjectMenuItem";
export default function ProjectList(){
//use context to pass state from root component
const {allProjects, selectedProject, currentTask} = React.useContext(AllContext);
const [allProjectsCopy, setAllProjects] = allProjects;
//make state for conditional rendering
const[displayProjects, setDisplayProjects] = React.useState<any>(null);
let projects: any;
//run this only when state updates
React.useEffect(() => {
console.log('Hello from the Project List component!', allProjectsCopy);
if(allProjectsCopy !== null){
console.log('allProjectsCopy updated and is NOT null.');
projects = Object.keys(allProjectsCopy);
let displayProjectsUpdate = projects.map((item: any)=>(
<div key={item}>
<ProjectMenuItem projectName={item} />
</div>
)
);
setDisplayProjects(displayProjectsUpdate);
}
}, [allProjectsCopy]);
return(
<div className="project-list flexbox">
<p>project components go here</p>
{(displayProjects !== null) ? displayProjects : null}
</div>
)
}
Child Components:
export default function ProjectMenuItem(props: any) {
return(
<div className="project-menu-item flexbox">
<img src="https://img.icons8.com/material-two-tone/48/null/overview-pages-3.png" alt="icon" role="button" tabIndex={0} aria-label="Clickable image" />
<h3 className="project-item-heading">{props.projectName}</h3>
<img src="https://img.icons8.com/ios/50/null/edit-property.png" alt="icon" role="button" tabIndex={0} aria-label="Clickable image"/>
</div>
);
}
英文:
So this is probably not best practices, but this is how I ended up solving the problem yesterday. I am very open to feedback on things I could do better. I wrote it this way because it was the only way I could have my code work when it loaded from an empty (null) state when the app renders at first, because trying to run Object.keys()
or map()
on things that were null
was making my app crash, so I wrapped my logic in a hook and conditional rendering to make things work.
Parent Component:
import React, {useContext} from "react";
import { AllContext } from "../App";
import ProjectMenuItem from "./ProjectMenuItem";
export default function ProjectList(){
//use context to pass state from root component
const {allProjects, selectedProject, currentTask} = React.useContext(AllContext);
const [allProjectsCopy, setAllProjects] = allProjects;
//make state for conditional rendering
const[displayProjects, setDisplayProjects] = React.useState<any>(null);
let projects: any;
//run this only when state updates
React.useEffect(() => {
console.log('Hello from the Project List component!', allProjectsCopy);
if(allProjectsCopy !== null){
console.log('allProjectsCopy updated and is NOT null.');
projects = Object.keys(allProjectsCopy);
let displayProjectsUpdate = projects.map((item: any)=>(
<div key={item}>
<ProjectMenuItem projectName={item} />
</div>
)
);
setDisplayProjects(displayProjectsUpdate);
}
}, [allProjectsCopy]);
return(
<div className="project-list flexbox">
<p>project components go here</p>
{(displayProjects !== null) ? displayProjects : null}
</div>
)
}
Child Components:
export default function ProjectMenuItem(props: any) {
return(
<div className="project-menu-item flexbox">
<img src="https://img.icons8.com/material-two-tone/48/null/overview-pages-3.png" alt="icon" role="button" tabIndex={0} aria-label="Clickable image" />
<h3 className="project-item-heading">{props.projectName}</h3>
<img src="https://img.icons8.com/ios/50/null/edit-property.png" alt="icon" role="button" tabIndex={0} aria-label="Clickable image"/>
</div>
);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论