2

I have this JSON data I am trying to render as a list.

[{
    "id": "1",
    "name": "Bill"
}, {
    "id": "2",
    "name": "Sarah"
}]

I am trying to display the data such that I pass a title of the data and the id's as parameters from Parent class to Child class and the latter prints the data as list.

export default class Parent extends Component {
  render() {
    return (
        <div>
        <Child
          title={"Group 1"}
          options={this.props.data.name}
        />
        </div>
    );
  }
}

Here's my Child class:

export default class Child extends Component {
  render() {
    var data=this.props;
    var title=this.props.title;
    var name=this.props.name;
    return (
      <ul>
        <h4>{title}</h4>
        <div>{this.props.map(item=>
          <li>{name}</li>
        )}
        </div>
      </ul>
    );
  }
}

What I don't understand is how to use map in the Child class to display data. I know that I shouldn't write this.props.map(item=>. I am new to React and would appreciate some help fixing this.

moirK
  • 651
  • 3
  • 11
  • 34

4 Answers4

1

Well, first you have to use JSON.parse() on your json, so it becomes a javascript object, you also have to make sure you are passing the props to your Child right. Then, you can map like this:

this.props.data.map((item,i) => <li key={i}>{item.name}</li>)

Tiago Alves
  • 2,290
  • 13
  • 32
  • Is this the right way to pass an attribute of the JSON object, say "name" ?`{this.props.data.name}` – moirK Nov 14 '17 at 13:05
1

Just pass your data as prop from parent component and you can access it in child component.

let {Component} = React;

const data = [{
    "id": "1",
    "name": "Bill"
}, {
    "id": "2",
    "name": "Sarah"
}]

class Child extends Component {
  render() {
   let {data, title} = this.props;
   
    return (
      <ul>
        <h4>{title}</h4>
        <div>{data.map(item=>
          <li key={item.id}>{item.name}</li>
        )}
        </div>
      </ul>
    );
  }
}

class Parent extends Component {
  render() {
    return (
        <div>
          <Child
            title={"Group 1"}
            data={this.props.data}
          />
        </div>
    );
  }
}

ReactDOM.render(
  <Parent data={data} />,
  document.getElementById('app')
);
<script src="https://unpkg.com/react@16.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.1.0/umd/react-dom.development.js"></script>
<div id="app"></div>

You can also call map method inside parent component and for each element in your data render Child component. Also only li element can be direct child of ul.

let {Component} = React;
const data = [{"id": "1","name": "Bill"}, {"id": "2","name": "Sarah"}]

class Child extends Component {
  render() {
    let {name} = this.props;
    return <li>{name}</li>
  }
}

class Parent extends Component {
  render() {
    return (
        <div>
          <h1>{"Group 1"}</h1>
          <ul>{this.props.data.map(item => {
                return <Child key={item.id} {...item} />
              })}
          </ul>
        </div>
    );
  }
}

ReactDOM.render(
  <Parent data={data} />,
  document.getElementById('app')
);
<script src="https://unpkg.com/react@16.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.1.0/umd/react-dom.development.js"></script>
<div id="app"></div>

Update: for nested children you can React.cloneElement to dynamically add child nodes.

let {Component} = React;
const data = [{ "id": "1", "name": "Bill" }, { "id": "2", "name": "Sarah", "childnodes": [{ "id": "3", "name": "Some name", "childnodes": [{ "id": "4", "name": "Last" }]}] }]

class Child extends Component {
  render() {
    let {name} = this.props;
    return <li>
      {name}
      {this.props.children ? <ul>{this.props.children}</ul> : ''}
    </li>
  }
}

class Parent extends Component {
  constructor(props) {
    super(props);
    this.renderNodes = this.renderNodes.bind(this);
  }
  
  renderNodes(data) {
    return data.map(item => {
      let child = <Child key={item.id} {...item} />
      
      if(item.childnodes) {
        child = React.cloneElement(child, {
          children: this.renderNodes(item.childnodes)
        })
      }
      
      return child;
    })
  }

  render() {
    return (
        <div>
          <h1>{"Group 1"}</h1>
          <ul>{this.renderNodes(this.props.data)}</ul>
        </div>
    );
  }
}

ReactDOM.render(
  <Parent data={data} />,
  document.getElementById('app')
);
<script src="https://unpkg.com/react@16.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.1.0/umd/react-dom.development.js"></script>
<div id="app"></div>
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
  • Thanks a lot. Is it possible to modify the second approach for recursive check for example the child nodes like here but passing only `props.name` as argument in `Child` component: `[{ "id": "1", "name": "Bill" }, { "id": "2", "name": "Sarah", "childnodes": [{ "id": "3", "name": "4" }] }]` – moirK Nov 14 '17 at 13:52
0

You have a few issues with your code:

  1. You need to pass your JSON object down from your Parent to your Child-component. If it's called data, then do:

    <Child
      title={"Group 1"}
      options={this.props.data}
    />
    

    I suggest using data instead of options to prevent confusion, but that's up to you. Also, you cannot pass this.props.data.name because this.props.data is an array of objects.

  2. Now that options is an array, we can properly use map(). You were trying to use map() on this.props, which is incorrect. So try instead:

    <div>
      {this.props.map(item=>
        <li>{item.name}</li>
      )}
    </div>
    
  3. Also, <ul> elements can only have <li> as their direct children. You have placed <h4> and <div> elements as its children. Instead wrap those inside an <li> element.


Full demo:

const data = [
  {
    "id": "1",
    "name": "Bill"
  }, {
    "id": "2",
    "name": "Sarah"
  }
];

class Parent extends React.Component {
  render() {
    return (
      <div>
        <Child
          title={"Group 1"}
          options={this.props.data}
        />
      </div>
    );
  }
}

class Child extends React.Component {
  render() {
    var title = this.props.title;
    return (
      <div>
        <h4>{title}</h4>
        <ul>
          <li>
            <div>
              {this.props.options.map(item=>
                <li>{item.name}</li>
              )}
            </div>
          </li>
        </ul>
      </div>
    );
  }
}

ReactDOM.render(<Parent data={data} />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Chris
  • 57,622
  • 19
  • 111
  • 137
0

let's don't use map in the child... try this:

let data = [{
    "id": "1",
    "name": "Bill"
}, {
    "id": "2",
    "name": "Sarah"
}]

user map in parent component:

export default class Parent extends Component {
  render() {
    return (
        <div>
        <div>Group 1</div>
        <ul>
          {data.map(item => <Child
            options={item}
          />)}
        </ul>
        </div>
    );
  }
}

enhance the child component :

export default class Child extends Component {
  render() {
    let {options:{name}} = this.props
    return (
      <li>{name}</li>
    );
  }
}
challenger
  • 2,184
  • 1
  • 18
  • 25