bkula
bkula

Reputation: 559

Rendering a component multiple times with different props in React

I have one component that I'm trying to render four times, each time with different props. To be a little more succinct with my code and not actually write out the JSX for the component and its props each time, I was trying to use map to create different instances of the component. Right now, here's what that looks like:

import React, { Component } from 'react';
import Panel from './components/Panel';
import Results from './components/Results';
import Intro from './components/Intro';

const questionsMap = [0, 1, 2, 3];

class React extends Component {
    constructor (props) {
        super (props);
        this.state = {
            questions: ['question1', 'question2', 'question3', 'question4'],
            answers: ['answers1', 'answers2', 'answers3', 'answers4']
        }
        this.onSelect = this.onSelect.bind(this);
    }

    onSelect(value) {
        /* Some code for when buttons are clicked */
    }

    render () {
        return (
            <Intro />
            {questionsMap.map(i => {
            return <Panel question={this.state.questions.i} answers={this.state.answers.i} onSelect={this.onSelect} />
            })}
            <Results />
        );
    }
}

export default App;

Right now I'm getting an Unexpected token error pointing to the line under my render that starts with {questionsMap.map()}, aka the part where I'm trying to actually do the mapping I mentioned. I assume I'm using the wrong syntax to accomplish what I want?

Upvotes: 5

Views: 29656

Answers (6)

Revan99
Revan99

Reputation: 446

the problem is that you are returning more than one element which is invalid in react if you want to return more than one element you can either wrap them inside a div which makes an extra element on the DOM or you can use React fragment

case 1:

    render () {
        return (
            <>        //<React.fragment //either you the empty tag or the one with React.fragment they are both are valid
                <Intro />
                {questionsMap.map((_,i) => {
                    return <Panel question={this.state.questions[i]} answers= 
                {this.state.answers[i]} onSelect={this.onSelect} />})}
                <Results />
            </>       //</React.fragment> 
        );
    }

case 2:

    render () {
        return (
            <div>       
                <Intro />
                {questionsMap.map((_,i) => {
                    return <Panel question={this.state.questions[i]} answers= 
                {this.state.answers[i]} onSelect={this.onSelect} />})}
                <Results />
            </div> 
        );
    }

Upvotes: 0

Vasu Chawla
Vasu Chawla

Reputation: 53

return method expects only 1 child and in your example, it had 3 children ie:

  • Intro component
  • an array of <Panel>s
  • Result component.

to fix this, simplest way is to inclose within <div>...</div> tags

or in the case where this extra div hinders with the styling, you can simply enclose within <>...</> tags

Upvotes: 0

G4bri3l
G4bri3l

Reputation: 5146

Here is the correct syntax:

import React, { Component } from 'react';
import Panel from './components/Panel';
import Results from './components/Results';
import Intro from './components/Intro';

const questionsMap = [0, 1, 2, 3];

class React extends Component {
    constructor (props) {
        super (props);
        this.state = {
            questions: ['question1', 'question2', 'question3', 'question4'],
            answers: ['answers1', 'answers2', 'answers3', 'answers4']
        }
        this.onSelect = this.onSelect.bind(this);
    }

    onSelect(value) {
        /* Some code for when buttons are clicked */
    }

    render () {
        return (
            <div>
              <Intro />
              {questionsMap.map(i => {
              return <Panel question={this.state.questions[i]} answers={this.state.answers[i]} onSelect={this.onSelect} />
            })}
              <Results />
            </div>
        );
    }
}

export default App;

But there are a few things that are not exactly a good practice, I assume this is some sort of test so I don't expect that you will name one of your components React.

On top of that you could simply map through the state, I would change the code a bit like this:

import React, { Component } from 'react'; import Panel from './components/Panel'; import Results from './components/Results'; import Intro from './components/Intro';


class React extends Component {
    constructor (props) {
        super (props);
        this.state = {
            questions: ['question1', 'question2', 'question3', 'question4'],
            answers: ['answers1', 'answers2', 'answers3', 'answers4']
        }
        this.onSelect = this.onSelect.bind(this);
    }

    onSelect(value) {
        /* Some code for when buttons are clicked */
    }

    render () {
        return (
            <div>
              <Intro />
              {this.state.questions.map((question, questionIndex) => {
                return (<Panel
                question={question}
                answers={this.state.answers[questionIndex]}
                onSelect={this.onSelect} />
              )
            })}
              <Results />
            </div>
        );
    } }

export default App;

Alternatively you could have an array of objects with a field named question and another named answer, just to give you another idea.

Upvotes: 9

Jo&#227;o Vila&#231;a
Jo&#227;o Vila&#231;a

Reputation: 621

  1. Dont name your class React
  2. Unless you're using React 16 you need to wrap everything inside a div (on render method)
  3. You don't need questionsMap . You can just use the index that map gives you for free, the map function's first argument is the element, and the second will be the index.
  4. Deconstruct your questions and answer inside render, before the return, like so: `const { questions, answers } = this.state; For readability.
  5. Goodluck

Upvotes: 0

stackdumper
stackdumper

Reputation: 136

At first, render can return only one element. You should wrap your components with div. At second, this.state.questions.i is wrong syntax. Use this.state.questions[i]. Finally, I think there's the better approach:

return (
  <div>
    <Intro />
    {
      this.state.questions.map((question, i) => {
        return <Panel question={question} answers={this.state.answers[i]} onSelect={this.onSelect} />
      })
    }
    <Results />
  </div>
);

Upvotes: 0

Shrey Kejriwal
Shrey Kejriwal

Reputation: 736

You haven't used the map function correctly.

Please check with the below code

import React, { Component } from 'react';

import Panel from './components/Panel';
import Results from './components/Results';
import Intro from './components/Intro';

const questionsMap = [0, 1, 2, 3];

class React extends Component {
    constructor (props) {
        super (props);
        this.state = {
            questions: ['question1', 'question2', 'question3', 'question4'],
            answers: ['answers1', 'answers2', 'answers3', 'answers4']
        }
        this.onSelect = this.onSelect.bind(this);
    }

    onSelect(value) {
        /* Some code for when buttons are clicked */
    }

    render () {
        return (
            <Intro />
            {questionsMap.map((_,i) => {
            return <Panel question={this.state.questions[i]} answers={this.state.answers[i]} onSelect={this.onSelect} />
            })}
            <Results />
        );
    }
}

export default App;

1st argument in map is the value and second argument is index. Since, we don't need value from map so I have given it as _.

Upvotes: -1

Related Questions