从对象中动态生成 React 组件作为 props。

huangapple go评论75阅读模式
英文:

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(
        &lt;p&gt;{projectName}&lt;/p&gt;
    );

}

Parent Component:

export function ProjectList(){
    
    const projects = {
        project1: {
            title: &#39;Project 1&#39;,
        },
        project2: {
            title: &#39;Project 2&#39;,
        },
        project3: {
            title: &#39;Project 3&#39;,
        },
    };
    
    function generateProjectMenuItems() {
        const projectMenuItems = [];
        for (const project in projects) {
            const projectName: string = projects[project as keyof typeof projects].title;
            projectMenuItems.push(&lt;ProjectMenuItem projectName={projectName} /&gt;);
        }
        
        return projectMenuItems;
    }

    return(
        &lt;div className=&quot;project-list flexbox&quot;&gt;
            &lt;p&gt;project components go here&lt;/p&gt;
            {generateProjectMenuItems()}
            
        &lt;/div&gt;
    )
}

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 =&gt; &lt;span className=&quot;project-span&quot; key={Math.random().toString()}&gt;{el}&lt;/span&gt;);

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。当它尝试在 &lt;p&gt;{projectName}&lt;/p&gt; 中渲染它时,会给你报错 "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 &lt;p&gt;{projectName}&lt;/p&gt; 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 &quot;react&quot;;
import { AllContext } from &quot;../App&quot;;
import ProjectMenuItem from &quot;./ProjectMenuItem&quot;;


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&lt;any&gt;(null);
    let projects: any;



    //run this only when state updates
    React.useEffect(() =&gt; {
        console.log(&#39;Hello from the Project List component!&#39;, allProjectsCopy);
        if(allProjectsCopy !== null){
            console.log(&#39;allProjectsCopy updated and is NOT null.&#39;);
            projects = Object.keys(allProjectsCopy);
            let displayProjectsUpdate = projects.map((item: any)=&gt;(
                &lt;div key={item}&gt;
                    &lt;ProjectMenuItem projectName={item} /&gt;
                &lt;/div&gt;
                )
            );
            setDisplayProjects(displayProjectsUpdate);
        }
      }, [allProjectsCopy]);


    return(
        &lt;div className=&quot;project-list flexbox&quot;&gt;
            &lt;p&gt;project components go here&lt;/p&gt;
            {(displayProjects !== null) ? displayProjects : null}
            
        &lt;/div&gt;
    )
}

Child Components:

export default function ProjectMenuItem(props: any) {

    return(
        &lt;div className=&quot;project-menu-item flexbox&quot;&gt;
          &lt;img src=&quot;https://img.icons8.com/material-two-tone/48/null/overview-pages-3.png&quot; alt=&quot;icon&quot; role=&quot;button&quot; tabIndex={0} aria-label=&quot;Clickable image&quot; /&gt;
          &lt;h3 className=&quot;project-item-heading&quot;&gt;{props.projectName}&lt;/h3&gt;
          &lt;img src=&quot;https://img.icons8.com/ios/50/null/edit-property.png&quot; alt=&quot;icon&quot; role=&quot;button&quot; tabIndex={0} aria-label=&quot;Clickable image&quot;/&gt; 
        &lt;/div&gt;
    );


}

huangapple
  • 本文由 发表于 2023年2月6日 02:28:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/75354559.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定