" Objects are not valid as a React child (found: object with keys {})." - reactjs

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>
)
}
}

Related

React parsing an array of objects coming from an API

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>
)
}

How to pass data to props from state?

I'm learning React and have some troubles with using state and props. There is two files: App.js and component. In App.js i use axios to get JSON data from IP and store in a state. But I cannot pass the data to props through the state.
Here is App.js:
import React from 'react';
import axios from 'axios';
import Shutruk from './Shutruk';
const qwerty = {
"data": [
{
"_id": "5d1cb18e4af03042df6267c5",
"title": "Shutruk",
"description": "Shhhhhhhhhhhhh",
"date": "2019-07-03T13:45:50.850Z",
"__v": 0
},
{
"_id": "5d1cc27b37c9751001f5c12f",
"title": "Shilkhak",
"description": "Shilkhak-Inshushinak",
"date": "2019-07-03T14:58:03.797Z",
"__v": 0
},
{
"_id": "5d1cc45655780f11112a023f",
"title": "Унташ",
"description": "Untash-Napirisha",
"date": "2019-07-03T15:05:58.699Z",
"__v": 0
},
{
"_id": "5d1ef36c503601183b5f856f",
"title": "dgfdgfdhgf",
"description": "bbbbbbbbbbbbbbbbb",
"date": "2019-07-05T06:51:24.873Z",
"__v": 0
},
{
"_id": "5d1ef381503601183b5f8570",
"title": "qewytuytruytru",
"description": "jhfgasjdfgasjdfgjhsdf",
"date": "2019-07-05T06:51:45.761Z",
"__v": 0
}
]
};
class App extends React.Component {
state = {
data: []
}
componentDidMount() {
axios.get('http://localhost:5555/posts')
.then(res => {
const data = res.data;
this.setState({ data });
})
}
render() {
return (
<div>
<Shutruk name={ this.state.data.data[0].title }/>
</div>
)
}
}
export default App;
Here is the component:
import React from 'react';
class Shutruk extends React.Component {
render() {
return (
<div>
<h1>This is is {this.props.name}!</h1>
</div>
)
}
}
export default Shutruk;
I use axios to get data from backend, but when I insert it to props, it does'nt work. I create an array qwerty[] with the same data, and when I replace with:
return (
<div>
<Shutruk name={ qwerty.data[0].title }/>
</div>
)
it works correctly. What is the problem, if there is no difference between 'this.state.data' and 'qwerty'?
I checked with console.log and the result is same!
Thanks to everyone for any help!
This is because, axios and setState are asynchronous and when the component loads in componentDidMount, it takes a while to load data into state and since data.data[0] is still empty, it doesn't work. But when you use a const to declare it, it works as it is already present.
Instead of
<Shutruk name={ this.state.data.data[0].title }/>
Do:
renderShutruk = () => {
if (this.state.data.data !== undefined) {
return <Shutruk name={this.state.data.data[0].title} />
} else {
return null;
}
};
render() {
return (
<div>
{this.renderShutruk()}
</div>
);
}
Your App component is probably crashing when mounting while you're accessing an undefined state because your try to get this.state.data.data[0].title when data state equals []
Try replacing your App component like this to prevent access to an undefined state (I recommend doing this for all your asynchronous operations in the components):
class App extends React.Component {
state = {
data: [],
loading: true,
error: false,
}
componentDidMount() {
axios.get('http://localhost:5555/posts')
.then(res => {
const data = res.data.data; // get the data array instead of object
this.setState({ data, loading: false });
})
.catch(err => { // log request error and prevent access to undefined state
this.setState({ loading: false, error: true });
console.error(err);
}
render() {
if (this.state.loading) {
return(
<div>
<p> Loading... </p>
</div>
)
}
if (this.state.error || !this.state.data[0]) { // if request failed or data is empty don't try to access it either
return(
<div>
<p> An error occurred </p>
</div>
)
}
return (
<div>
<Shutruk name={ this.state.data[0].title }/> // access to data array state
</div>
)
}
}
export default App;
Just change this,
<Shutruk name={ this.state.data.data[0].title }/>
with,
{this.state.data ? <Shutruk name={ this.state.data[0].title }/> : null}
Update
If you are not getting data, then you must use async/await,
async componentDidMount() {
await axios.get('http://localhost:5555/posts')
.then(res => {
//console.log(res) => if `res` is {data:[...]} , then do this,
const data = res.data;
//console.log(res) => if `res` is {data: data: [...]} , then do this,
const data = res.data.data;
this.setState({ data });
})
}

static getDerivedStateFromProps does't behave like componentwillreceiveprops?

Hi I'm trying to implement search in child component , the parent component will get data from server and pass that data to child component
as props, now child component has to implement search on that data, I have used componentwillreceiveprops which is depreciated how can I implement
this without using componentwillreceiveprops, below is my code
working example on fiddle
class Parent extends React.Component{
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
// mimic api call
const data = [
{ key: 'react'}, { key: 'redux'},
{ key: 'javascript' }, { key: 'Ruby'} ,{key: 'angular'}
]
setTimeout(this.setState({data}), 3000);
}
render() {
return (
<React.Fragment>
<ChildComponent data = {this.state.data}/>
</React.Fragment>
)
}
}
class ChildComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentwillreceiveprops(nextProps){
this.setState({data: nextProps.data})
}
search(e){
console.log('props,', e.target.value)
let searchedData = this.props.data.filter(el => {
return el.key.startsWith(e.target.value)
})
this.setState({data: searchedData})
};
render(){
return(
<div>
search for (react, redux, angular, ruby)
<br/> <br/> <br/>
<input type = 'text' onChange={this.search.bind(this)}/>
{this.state.data.map(d => {
return (<div key={d.key}>{d.key}</div>)
})}
</div>
)
}
}
getDerivedStateFromProps is not a direct replacement for componentWillReceiveProps. Its just meant to update state in response to any update and unlike componentWillReceiveProps, getDerivedStateFromProps is triggered on every update either from child or from parent so you cannot simply update state without any conditional check. In order to update state if the props changed, you need to store the previous props in the state of child too or update the key of child so that it triggers a re-render
There are two possible approaches to this. Below is an example of first approach with getDerivedStateFromProps
import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
import "./styles.css";
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
// mimic api call
const data = [
{ key: "react" },
{ key: "redux" },
{ key: "javascript" },
{ key: "Ruby" },
{ key: "angular" }
];
setTimeout(() => {
this.setState({ data });
setTimeout(() => {
this.setState(prev => ({ data: [...prev.data, { key: "Golang" }] }));
}, 3000);
}, 3000);
}
render() {
return (
<React.Fragment>
<ChildComponent data={this.state.data} />
</React.Fragment>
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
static getDerivedStateFromProps(props, state) {
if (!_.isEqual(props.data, state.prevData)) {
return {
data: props.data,
prevData: state.data
};
} else {
return {
prevData: props.data
};
}
}
search(e) {
console.log("props,", e.target.value);
let searchedData = this.props.data.filter(el => {
return el.key.startsWith(e.target.value);
});
this.setState({ data: searchedData });
}
render() {
return (
<div>
search for (react, redux, angular, ruby)
<br /> <br /> <br />
<input type="text" onChange={this.search.bind(this)} />
{this.state.data.map(d => {
return <div key={d.key}>{d.key}</div>;
})}
</div>
);
}
}
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
Working DEMO
Second approach involves changing the key of the child component instead of implementing getDerivedStateFromProps
import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
import "./styles.css";
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
keyData: 0
};
}
componentDidMount() {
// mimic api call
const data = [
{ key: "react" },
{ key: "redux" },
{ key: "javascript" },
{ key: "Ruby" },
{ key: "angular" }
];
setTimeout(() => {
this.setState(prev => ({ data, keyData: (prev.keyData + 1) % 10 }));
setTimeout(() => {
this.setState(prev => ({
data: [...prev.data, { key: "Golang" }],
keyData: (prev.keyData + 1) % 10
}));
}, 3000);
}, 3000);
}
render() {
return (
<React.Fragment>
<ChildComponent data={this.state.data} key={this.state.keyData} />
</React.Fragment>
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.data
};
}
search(e) {
console.log("props,", e.target.value);
let searchedData = this.props.data.filter(el => {
return el.key.startsWith(e.target.value);
});
this.setState({ data: searchedData });
}
render() {
return (
<div>
search for (react, redux, angular, ruby)
<br /> <br /> <br />
<input type="text" onChange={this.search.bind(this)} />
{this.state.data.map(d => {
return <div key={d.key}>{d.key}</div>;
})}
</div>
);
}
}
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
Working DEMO
You can go ahead with the second method when you know that you will have quite a few updates in the child component whereas update from parent will be less frequent and the props that you have to compare getDerivedStateFromProps is nested . In such as case implementing getDerivedStateFromProps will be less performant than updating the key since you will need to perform expensive computation on each render.
To implement your componentWillReceiveProps() behavior using the new getDerivedStateFromProps() method, you can replace your current componentwillreceiveprops() hook with this:
static getDerivedStateFromProps(nextProps, state){
/* Return the new state object that should result from nextProps */
return { data : nextProps.data }
}
The getDerivedStateFromProps() will be called before your component is rendered and, if a non-null value is returned, then that return value will become the state of the component.
In your case, the state of the <ChildComponent> component has only one data field which is populated directly from props, so returning { data : nextProps.data } would be sufficent to update the data state field to match the incoming data prop.
The general idea is that you can use this method to update a component's state based on changing/incoming props.
See this documentation for more information on getDerivedStateFromProps() - hope that helps!
Update
Also on another note, it seems the way <Parent> is updating state via the setTimeout method is incorrect. You should update it as follows:
// Incorrect: setTimeout(this.setState({data}), 3000);
setTimeout(() => this.setState({data}), 3000);

React - Data not found

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 });
}

React rendering nested json

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.

Resources