Advertisement
If you have a new account but are having problems posting or verifying your account, please email us on hello@boards.ie for help. Thanks :)
Hello all! Please ensure that you are posting a new thread or question in the appropriate forum. The Feedback forum is overwhelmed with questions that are having to be moved elsewhere. If you need help to verify your account contact hello@boards.ie
Hi there,
There is an issue with role permissions that is being worked on at the moment.
If you are having trouble with access or permissions on regional forums please post here to get access: https://www.boards.ie/discussion/2058365403/you-do-not-have-permission-for-that#latest

React: Component containing other components

  • 07-05-2021 9:17am
    #1
    Registered Users, Registered Users 2 Posts: 4,620 ✭✭✭


    Hi guys,

    Currently have an app that uses two components <Header> and <Content> to display the name of a course and some of its content.

    This is the output from the app

    Half Stack application development
    Fundamentals of React10

    This is the code

    import React from 'react'


    const Header = (props) => {

    return (
    <div>
    <h1>{props.course}</h1>
    </div>
    )
    }

    const Content = (props) => {

    return (
    <div>
    <p>{props.part1}</p>
    </div>
    )
    }


    const App = (props) => {
    const course = {
    name: 'Half Stack application development',
    parts: [
    {
    name: 'Fundamentals of React',
    exercises: 10
    },
    {
    name: 'Using props to pass data',
    exercises: 7
    },
    {
    name: 'State of a component',
    exercises: 14
    }
    ]
    }

    return (
    <div>
    <Header course={course.name}/>
    <Content part1 = {course.parts[0].name + course.parts[0].exercises} />
    </div>

    )
    }

    export default App


    All I want to do is create a new component <Course> that contains the two other components <Header> and <Content> and gives the same output I had originally.

    This is the component I created

    const Course = (props) => {
    return (
    <div>
    <Header></Header>
    <Content></Content>
    </div>
    )
    }

    and updated the App code like this

    return (
    <div>
    <Course course={course} />
    </div>
    )
    }

    I just get a blank screen, I'm assuming I'm not passing the data between the components correctly but can't figure out a solution.

    Any help appreciated.


Comments

  • Registered Users, Registered Users 2 Posts: 3,945 ✭✭✭Anima


    const Course = (props) => {
      return (
        <div>
          <Header></Header>
          <Content></Content>
        </div>
      )
    }
    

    You're not sending any data to header or content?


  • Registered Users, Registered Users 2 Posts: 4,620 ✭✭✭enfant terrible


    Anima wrote: »
    const Course = (props) => {
      return (
        <div>
          <Header></Header>
          <Content></Content>
        </div>
      )
    }
    

    You're not sending any data to header or content?

    Thought it would use the data from the header component that's already created.

    How would I get the data from the header component into the new Course component?

    Basically all I want to do is use the two components I already have in a single component.


  • Registered Users, Registered Users 2 Posts: 3,945 ✭✭✭Anima


    You have to be explicit about passing data if you use props. Something like react context can be less so.

    You have to manually pass the props down each level which is a bit annoying and causes it's own set of problems with excess re-renders etc. For larger apps you probably would end up using redux or local contexts which adds a small bit of indirection but feels a bit better when using a lot of data.


  • Registered Users, Registered Users 2 Posts: 4,620 ✭✭✭enfant terrible


    Anima wrote: »
    You have to be explicit about passing data if you use props. Something like react context can be less so.

    You have to manually pass the props down each level which is a bit annoying and causes it's own set of problems with excess re-renders etc. For larger apps you probably would end up using redux or local contexts which adds a small bit of indirection but feels a bit better when using a lot of data.

    How would I manually pass the props down?

    This is my Course component currently

    const Course = (props) => {
    return (
    <div>
    <Header></Header>
    <Content></Content>
    </div>
    )
    }

    This is my Header Component, I put "Test" in front of my {props.course} just to see if it would show up on screen and it did, but I'm not getting the props.course data.

    const Header = (props) => {

    return (
    <div>
    <h1>Test{props.course}</h1>
    </div>
    )
    }


    If I change props.course to props.course.name I get "Cannot read property 'name' of undefined"

    return (
    <div>
    <h1>Test{props.course.name}</h1>
    </div>
    )
    }


    const App = (props) => {
    const course = {
    name: 'Half Stack application development',
    parts: [
    {
    name: 'Fundamentals of React',
    exercises: 10
    },
    {
    name: 'Using props to pass data',
    exercises: 7
    },
    {
    name: 'State of a component',
    exercises: 14
    }
    ]
    }


  • Registered Users, Registered Users 2 Posts: 9,383 ✭✭✭S.M.B.


    Your amended example is missing the original functionality found in these two lines.

    <Header course={course.name}/>
    <Content part1 = {course.parts[0].name + course.parts[0].exercises} />

    I believe you would need to add something like the following to your new course component.

    <Header course={props. course.name}/>
    <Content part1 = {props. course.parts[0].name + course.parts[0].exercises} />


  • Advertisement
  • Registered Users, Registered Users 2 Posts: 4,620 ✭✭✭enfant terrible


    That worked thanks S.M.B and Anima, I made a meal of that :)


  • Registered Users, Registered Users 2 Posts: 4,620 ✭✭✭enfant terrible


    I'm now trying to render the parts of the course using the map method like below

    const Course = (props) => {
    return (
    <div>
    <Header course={props.course.name}/>
    <Content parts = {props.course.parts.map(part =>
    <li>
    {props.course.parts.name}
    </li>)} />
    </div>
    )
    }

    const Content = (props) => {

    return (
    <div>
    <p>{props.parts}</p>
    </div>
    )
    }


    Getting no errors but no parts at all:

    Half Stack application development
    .
    .
    .

    Any ideas?

    const App = () => {
    const course = {
    name: 'Half Stack application development',
    parts: [
    {
    name: 'Fundamentals of React',
    exercises: 10
    },
    {
    name: 'Using props to pass data',
    exercises: 7
    },
    {
    name: 'State of a component',
    exercises: 14
    }
    ]
    }


  • Registered Users, Registered Users 2 Posts: 3,945 ✭✭✭Anima


    <Content parts = {props.course.parts.map(part =>
      <li>
        {props.course.parts.name}
      </li>)} />
    

    This is probably not what you want to be doing. You should be passing data to components, not other components. At least most of the time anyway.

    In the mapping above, you're also not using the "part" argument but the same "props.courses.parts.name" each time. I'd try and think about the data flow of this app instead of writing code straight away.

    In general, react apps behave as if there is a data source that is static at the moment of rendering and you're just using this data to build a UI/DOM tree. Try and think about the components and the data and how they compose together. It should read pretty fluently and if not, then maybe something isn't represented as well as it could be.


  • Registered Users, Registered Users 2 Posts: 4,620 ✭✭✭enfant terrible


    Anima wrote: »
    <Content parts = {props.course.parts.map(part =>
      <li>
        {props.course.parts.name}
      </li>)} />
    

    This is probably not what you want to be doing. You should be passing data to components, not other components. At least most of the time anyway.

    In the mapping above, you're also not using the "part" argument but the same "props.courses.parts.name" each time. I'd try and think about the data flow of this app instead of writing code straight away.

    In general, react apps behave as if there is a data source that is static at the moment of rendering and you're just using this data to build a UI/DOM tree. Try and think about the components and the data and how they compose together. It should read pretty fluently and if not, then maybe something isn't represented as well as it could be.

    I'm assuming the question was asking me to create a component containing other components, here's what is said

    Define a component responsible for formatting a single course called Course.

    The component structure of the application can be, for example, the following:

    App
    Course
    Header
    Content
    Part
    Part
    ...
    Hence, the Course component contains the components defined in the previous part, which are responsible for rendering the course name and its parts.

    The rendered page can, for example, look as follows:
    Half Stack application development
    Fundamentals of React10

    What do you mean by "not using part argument" exactly?

    Thanks for the help.


  • Registered Users, Registered Users 2 Posts: 9,383 ✭✭✭S.M.B.


    As Anima pointed out in the code snippet above, you should be only passing the parts data to your content component and the mapping should then be done in the latter.

    They also highlighted how your mapping function is not correct even if moved. How familiar are you with vanilla JS?


  • Advertisement
  • Registered Users, Registered Users 2 Posts: 4,620 ✭✭✭enfant terrible


    S.M.B. wrote: »
    As Anima pointed out in the code snippet above, you should be only passing the parts data to your content component and the mapping should then be done in the latter.

    Not too familiar, have read Head First Javascript and done a few simple projects.

    So here:
    <Content parts = {props.course.parts.map(part =>
    <li>
    {props.course.parts.name}
    </li>)} />

    I should just be passing the parts data like so?

    <Content parts = {props.course.parts} />

    And the mapping should be done in App component?

    For the mapping I was following the example given in the question which is as follows:

    const notes = [
    {
    id: 1,
    content: 'HTML is easy',
    date: '2019-05-30T17:30:31.098Z',
    important: true
    },
    {
    id: 2,
    content: 'Browser can execute only JavaScript',
    date: '2019-05-30T18:39:34.091Z',
    important: false
    },
    {
    id: 3,
    content: 'GET and POST are the most important methods of HTTP protocol',
    date: '2019-05-30T19:20:14.298Z',
    important: true
    }
    ]

    ReactDOM.render(
    <App notes={notes} />,
    document.getElementById('root')
    )


    const App = (props) => {
    const { notes } = props

    return (
    <div>
    <h1>Notes</h1>
    <ul>
    {notes.map(note => <li>{note.content}</li>)}
    </ul>
    </div>
    )
    }

    Thanks again for help


  • Registered Users, Registered Users 2 Posts: 6,286 ✭✭✭Talisman


    Your data has the following structure:
    course = {
        name: 'Half Stack application development',
        parts: [
            {
                name: 'Fundamentals of React',
                exercises: 10
            },
            {
                name: 'Using props to pass data',
                exercises: 7
            },
            {
                name: 'State of a component',
                exercises: 14
            }
        ]
    }
    

    'course.parts' is an array which is why you can use the map function to create the content for each entry. Inside the the anonymous function you are passing an entry from the 'parts' array and giving it the label 'part'. Each entry is an object with two keys, 'name' and 'exercises', so within the function you are only going to reference 'part.name' and/or 'part.exercises'.

    So instead of referencing the content as
    {props.course.parts.name}
    

    I would expect the function to instead use
    {part.name}
    


  • Registered Users, Registered Users 2 Posts: 9,383 ✭✭✭S.M.B.


    Not too familiar, have read Head First Javascript and done a few simple projects.

    So here:
    <Content parts = {props.course.parts.map(part =>
    <li>
    {props.course.parts.name}
    </li>)} />

    I should just be passing the parts data like so?

    <Content parts = {props.course.parts} />

    And the mapping should be done in App component?
    You can do the mapping in your Content component.
    For the mapping I was following the example given in the question which is as follows:


    const App = (props) => {
    const { notes } = props

    return (
    <div>
    <h1>Notes</h1>
    <ul>
    {notes.map(note => <li>{note.content}</li>)}
    </ul>
    </div>
    )
    }

    Thanks again for help
    You can see they map through the notes in the App component and not in the way you had done it in your previous version where you did this.

    [HTML]
    <Content parts = {props.course.parts.map(part =>
    <li>
    {props.course.parts.name}
    </li>)} />[/HTML]


  • Registered Users, Registered Users 2 Posts: 4,620 ✭✭✭enfant terrible


    Talisman wrote: »
    Your data has the following structure:
    course = {
        name: 'Half Stack application development',
        parts: [
            {
                name: 'Fundamentals of React',
                exercises: 10
            },
            {
                name: 'Using props to pass data',
                exercises: 7
            },
            {
                name: 'State of a component',
                exercises: 14
            }
        ]
    }
    

    'course.parts' is an array which is why you can use the map function to create the content for each entry. Inside the the anonymous function you are passing an entry from the 'parts' array and giving it the label 'part'. Each entry is an object with two keys, 'name' and 'exercises', so within the function you are only going to reference 'part.name' and/or 'part.exercises'.

    So instead of referencing the content as
    {props.course.parts.name}
    

    I would expect the function to instead use
    {part.name}
    

    That change worked, thank you Talisman


Advertisement