I have some json looking like this
[
{
"name": "Hello Kitty",
"items": [
{
"name": "Kitty Muu Muu"
},
{
"name": "Kitty smack"
}
]
},
{
"name": "Hello Pussy",
"items": [
{
"name": "World",
"items": [
{
"name": "Hello Pussy world"
}
]
}
]
}
]
it's nested JSON and my question is; where should the recursiveness be handled to render this?
I have 2 components
List component that calls the data and posts data changes back to the server
Item component that renders a single item
Should I handle this at the list level or item level?
The item renders an input field you can update and this should be possible both at parent and child level
You can use Item to show child items(in React you can do it with children property), however main work will be in List, for example
class Item extends React.Component {
render() {
return <li>
{ this.props.name }
{ this.props.children }
</li>
}
}
class List extends React.Component {
constructor() {
super();
}
list(data) {
const children = (items) => {
if (items) {
return <ul>{ this.list(items) }</ul>
}
}
return data.map((node, index) => {
return <Item key={ node.id } name={ node.name }>
{ children(node.items) }
</Item>
});
}
render() {
return <ul>
{ this.list(this.props.data) }
</ul>
}
}
Example
I would do it in List component. If your List component is large, i would put them in another helper file to require it.
Related
I'm quite new with react, I'm trying to hit an API and I'm getting this response. I need to go over the array and show the elements in a table:
{
"people": [
{
"id": "1",
"name": "philip",
"age": 25,
"timestamp": "2020-10-17T21:59:50.151"
},
{
"id": "2",
"name": "philip2",
"age": 26,
"timestamp": "2020-10-17T21:59:50.152"
},
{
"id": "3",
"name": "philip3",
"age": 27,
"timestamp": "2020-10-17T21:59:50.153"
},
]
}
I'm hitting and getting response from the api correctly but I have some issues trying to parse it.
import React, { Component } from 'react';
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
people: []
}
}
componentDidMount() {
fetch('/local/api/people')
.then(res => res.json())
.then(json => json.people)
.then(people => this.setState({'people': people}))
}
render() {
return (
<div className="App">
{this.state.people}
Here I'd need to go over the array and show all the elements
</div>
);
}
}
export default App;
Error: Objects are not valid as a React child (found: object with keys ....... If you meant to render a collection of children, use an array instead.
I tried a lot of things but nothing worked so far
you have to map over the array inside the return like the code below and I am passing a key that's a react way to identify the element
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
and also am checking when the component renders that I will only show the list when the state people array length is true means not 0
import React, { Component } from "react";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
people: [],
};
}
componentDidMount() {
fetch("/local/api/people")
.then((res) => res.json())
.then((json) => json.people)
.then((people) => this.setState({ people: people }));
}
render() {
return (
<div className="App">
{this.state.people.length && this.state.people.map((element, key) => {
return (
<div key={key}>
<span>{element.id}</span>
<span>{element.name}</span>
<span>{element.age}</span>
<span>{element.timestamp}</span>
</div>
);
})}
</div>
);
}
}
export default App;
You can create a member function that maps over the array and creates and returns jsx for each table row. You can then call this function inside a table body tag.
renderTableData() {
return this.state.people.map((person, index) => {
const { id, name, age } = person //destructuring
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{age}</td>
</tr>
)
})
}
render() {
return (
<div className="App">
<table id='people'>
<tbody>
{this.renderTableData()}
</tbody>
</table>
</div>
)
}
A newbie in React here. I'm using axios to retrieve this object requested to my Django server rest framework Api:
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"url": "http://localhost:8000/blog/api/categories/1/",
"title": "Django",
"slug": "django"
},
{
"id": 2,
"url": "http://localhost:8000/blog/api/categories/2/",
"title": "Git",
"slug": "git"
},
{
"id": 3,
"url": "http://localhost:8000/blog/api/categories/3/",
"title": "Introduction to docker",
"slug": "introduction-to-docker"
}
]
}
So far I've been able to store those variables in separate arrays. This is the implementation of my FetchDemo.js:
import React, {Component} from 'react';
import axios from 'axios';
{/* https://daveceddia.com/ajax-requests-in-react/ */}
class FetchDemo extends Component {
state = {
urls: []
}
state = {
titles: []
}
state = {
slugs: []
}
componentDidMount() {
axios.get(`${this.props.url}${this.props.path}`).then(res => {
const urls = res.data.results.map(num => num.url);
const titles = res.data.results.map(num => num.title);
const slugs = res.data.results.map(num => num.slug);
this.setState( {urls} );
this.setState( {titles} );
this.setState( {slugs} );
});
}
render() {
return (
<div>
<h1>{`${this.props.url}${this.props.path}`}</h1>
<ul>
/// How to generate the JSX objects? ///
</ul>
</div>
);
}
}
export default FetchDemo;
Is it possible to build in the axios request a state array from this JSON object like the next one instead? How do I generate JSX with this new state categories?
this.setState({
categories: [
{url: "http://localhost:8000/blog/api/categories/1/", title: "Django", slug: "django"},
{url: "http://localhost:8000/blog/api/categories/2/", title: "Git", slug: "git"},
{url: "http://localhost:8000/blog/api/categories/3/", title: "Introduction to Docker", slug: "introduction-to-docker"},
]
}
)
I wish I knew more React and JavaScript. Any help is appreciated, thanks in advance.
There are a few things to note here.
state is a single object with multiple keys.
setState() is a single async transaction. When you are updating your state, you should update it in one go.
You can do things in a simpler way if I understand your question right. You don't have to store the values in multiple keys. It can be stored in a single key categories.
import React, { Component } from 'react';
import axios from 'axios';
class FetchDemo extends Component {
constructor(props) {
super(props);
this.state = {
categories = []
}
}
componentDidMount() {
axios.get(`${this.props.url}${this.props.path}`).then((res) => {
const urls = res.data.results.map((num) => num.url);
const titles = res.data.results.map((num) => num.title);
const slugs = res.data.results.map((num) => num.slug);
this.setState({
categories: res.data.results
});
});
}
render() {
const { categories } = this.state;
return (
<div>
<h1>{`${this.props.url}${this.props.path}`}</h1>
<ul>
{categories.length ?
categories.map(cat => (
<li key={cat.id}>
<div>{cat.title}</div>
</li>
))
:
(<li>categories not loaded yet</li>)}
</ul>
</div>
);
}
}
export default FetchDemo;
class Home extends PureComponent {
render() {
const { proList } = this.props;
return (
<HomeWrap>
<Banner />
<Profile />
<div className="projectWrap">
<div className="hotRecommended">
{
this.hotRecommendList(proList)
}
</div>
</div>
</HomeWrap>
)
}
hotRecommendList(list) {
let hotTarget = list.filter(item => item.tag === 'hot');
return hotTarget.map((target, index) => {
return (
<Project key={index} />
)
})
}
}
the error is:
Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
get the json file proList like this:
[
{
"id": 1,
"img": "https://gw.alicdn.com/tfs/TB1wx_RQFXXXXbGXFXXXXXXXXXX-250-125.png_200x200q85s150.jpg_.webp",
"desc": "英国证券交易所,国家:英国, 适合人群:本科毕业生 专科毕业生,时间:2019.01.08-2019.02.08",
"tag": "hot"
},
{
"id": 2,
"img": "https://gw.alicdn.com/tfs/TB1wx_RQFXXXXbGXFXXXXXXXXXX-250-125.png_200x200q85s150.jpg_.webp",
"desc": "美国白宫,国家:美国, 适合人群:美籍华人 老外, 时间:2019.01.08-2019.02.08",
"tag": "quality"
}
]
Now I think I know, The Project component get an object but render it incorrect,
finially the project component like this:
class Project extends PureComponent {
constructor(props) {
super(props);
console.log('projcet component props', props);
}
render() {
let { target } = this.props;
console.log('target', target);
return (
<ProjectWrap>
<div>{target.desc}</div>
</ProjectWrap>
)
}
}
I have a main component Resume and a child called Header:
class Resume extends Component {
constructor(props) {
super();
this.state = {
data: {}
}
}
componentDidMount() {
this.setState( this.state = data);
}
render() {
return (
<div className="Resume">
<Header data={this.state.header} />
<Skills data={this.state.skills} />
</div>
);
}
}
export default Resume;
class Header extends Component {
render() {
return (
<div className="header">
<h1>{JSON.stringify(this.props.data.title)}</h1>
</div>
);
}
}
My simple sample data so far is:
{
"header": {
"title": "Tim Smith's Resume"
},
"skills": [
{"name": "HTML", "duration": 14}
]
}
Why am I getting: TypeError: Cannot read property 'title' of undefined
No need to use JSON#stringify your title property is already a string.
In your componentDidMount is not like that we are setting state. Try to use
componentDidMount()
const data = {}; // Define data or get it like you want
this.setState({
...data
});
}
In your constructor where you initialize your state. You have to do this.state = { header: {}, skills: {} } not what you done.
It looks like you're not setting the state of the <Resume/> component correctly. Try the following adjustment:
componentDidMount() {
// This will set the `data` field of your component state, to the
// value of the `data` variable.
this.setState({ data : data });
}
I'm learning React-Redux and have come to an issue when trying to render out a side navigation "sub section" item from a nested object.
Here is the component:
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
class SideNav extends Component {
render() {
return (
<div className="sidenav-cont">
<ul className="top-level-menu">
{
this.props.reducerSidenav.map((menuItem) => {
return (
<li key={menuItem.catId}>
{menuItem.parentCat}
<ul className="bottom-level-menu">
{
this.props.reducerSidenav.subCats.map((subMenuItem) => {
<li key={subMenuItem.subCatId}>{subMenuItem.subCatLabel}</li>
})
}
</ul>
</li>
)
})
}
</ul>
</div>
)
}
}
function mapStateToProps(state) {
return {
reducerSidenav:state.reducerSidenav
};
}
export default connect(mapStateToProps)(SideNav);
if I remove the second nested <ul> with the className "bottom-level-menu" then the app renders out my "parent section" list item no problem. But it's when I attempt to re-create the same function but for a nested objected that I get errors. Here is my reducer with the sidenav menu object:
export default function(){
return [
{
catId:"parMenu1",
parentCat:"Bass",
subCats:[
{
subCatId:"subMenu1",
subCatLabel:"base1"
},
{
subCatId:"subMenu2",
subCatLabel:"base2"
},
{
subCatId:"subMenu3",
subCatLabel:"base3"
},
{
subCatId:"subMenu4",
subCatLabel:"base4"
}
]
},
{
catId:"parMenu2",
parentCat:"treb",
subCats:[
{
subCatId:"subMenu1",
subCatLabel:"treb1"
},
{
subCatId:"subMenu2",
subCatLabel:"treb2"
},
{
subCatId:"subMenu3",
subCatLabel:"treb3"
},
{
subCatId:"subMenu4",
subCatLabel:"treb4"
}
]
},
{
catId:"parMenu3",
parentCat:"drums",
subCats:[
{
subCatId:"subMenu1",
subCatLabel:"drums1"
},
{
subCatId:"subMenu2",
subCatLabel:"drums2"
},
{
subCatId:"subMenu3",
subCatLabel:"drums3"
},
{
subCatId:"subMenu4",
subCatLabel:"drums4"
}
]
},
]
}
As you can see the nested object to be used for the sub navigation items is titled subCats. I would have thought that I could access the object within another object by referring to the sidenav reducer state like so: this.props.reducerSidenav.subCats.map((subMenuItem) => {... much like I did for the parent categories but I'm getting the "Cannot read property 'map' of undefined" error in console. Where have I gone wrong?
There is a bug in your code.
this.props.reducerSidenav.subCats.map((subMenuItem) => {
<li key={subMenuItem.subCatId}>{subMenuItem.subCatLabel}</li>
})
reducerSidenav is a table, probably you wanted to have something like this
menuItem.subCats.map((subMenuItem) => {
<li key={subMenuItem.subCatId}>{subMenuItem.subCatLabel}</li>
})
Quote your keys?
export default function(){
return [
{
"catId":"parMenu1",
"parentCat":"Bass",
"subCats":[
{
"subCatId":"subMenu1",
"subCatLabel":"base1"
},
{
"subCatId":"subMenu2",
"subCatLabel":"base2"
},
{
"subCatId":"subMenu3",
"subCatLabel":"base3"
},
{
"subCatId":"subMenu4",
"subCatLabel":"base4"
}
]
},
{
"catId":"parMenu2",
"parentCat":"treb",
"subCats":[
{
"subCatId":"subMenu1",
"subCatLabel":"treb1"
},
{
"subCatId":"subMenu2",
"subCatLabel":"treb2"
},
{
"subCatId":"subMenu3",
"subCatLabel":"treb3"
},
{
"subCatId":"subMenu4",
"subCatLabel":"treb4"
}
]
},
{
"catId":"parMenu3",
"parentCat":"drums",
"subCats":[
{
"subCatId":"subMenu1",
"subCatLabel":"drums1"
},
{
"subCatId":"subMenu2",
"subCatLabel":"drums2"
},
{
"subCatId":"subMenu3",
"subCatLabel":"drums3"
},
{
"subCatId":"subMenu4",
"subCatLabel":"drums4"
}
]
},
]
}
I think the "this" keyword inside your first map() function does not reference what you think it does. You need to bind "this" to the map() function to access the props in the outer object:
this.props.reducerSidenav.map((menuItem) => {
return (
<li key={menuItem.catId}>
{menuItem.parentCat}
<ul className="bottom-level-menu">
{
this.props.reducerSidenav.subCats.map((subMenuItem) => {
<li key={subMenuItem.subCatId}>{subMenuItem.subCatLabel}</li>
})
}
</ul>
</li>
)
}.bind(this))
}