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 });
}
Related
My Objective is to display the name which is inside the object. here is the snippet attached for your reference.
it contains:
{
id: "12497wewrf5144",
name: "ABC",
isVisible: "false"
}
I have tried in this way:
import React, { Component } from "react";
class Demo extends Component {
constructor(props: Props) {
super(props);
this.state = {
demo: {}
};
}
componentDidMount() {
axios.get('/api/random')
.then(res => {
this.setState({demo: res.data})
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<div>
<h1>{this.state.demo.name}</h1>
</div>
);
}
}
export default Demo;
But i'm not getting the value of name.
We use map for multiple objects in array, but do we need to map even for single array?
i have tried giving like "this.state.demo[0].name", but this one also not working
Can anyone help me in this query?
Assuming your API response is like following
data =[
{id: "12497wewrf5144", name: "ABC", isVisible: "false"},
{id: "12497wewrf5255", name: "CBD", isVisible: "true"}
];
Render Response Data
class First extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{id: "12497wewrf5144", name: "ABC", isVisible: "false"},
{id: "12497wewrf5255", name: "CBD", isVisible: "true"}
];,
};
}
render() {
return (
<ul>
{this.state.data.map(d => <li key={d.name}>{d.name}</li>)}
</ul>
);
}
}
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);
I have a component Data and its child component BarChart.
Data component looks as following:
export default class Data extends Component {
constructor(props) {
super(props);
this.state = {
data: {
labels: [],
datasets: [{
label: "",
data: [],
backgroundColor: ''
}]
}
}
}
componentWillMount() {
this.geData();
}
geData = () => {
let labelsData = someContent;
let datasets = otherContentl;
this.setState({data: {...this.state.data, labels: labelsData, datasets: datasets}}, ()=>{console.log(this.state.data)});
}
render(){
return (
<BarChart data={this.state.data} />
);
}
}
When I check the result of console.log(this.state.data) in getData function, it prints out the correct data.
However, when I receive the props in BarChart component, I only receive datasets key filled with the correct data, but labels key is an empty array.
export default class BarChart extends Component {
constructor(props) {
super(props);
console.log(props);
}
componentWillMount() {
this.setState({chartData: this.props.data});
}
render() {
return (
<div className="barChart">
<Bar
data={this.state.chartData}
/>
</div>
);
}
}
Why does that happen? How can it be fixed?
What I had in BarChart component is:
constructor(props) {
super(props);
this.state = {
chartData: {}
}
}
componentWillMount() {
this.setState({chartData: this.props.data});
}
render() {
return (
<div className="barChart">
<Bar
data={this.state.chartData}
/>
</div>
);
}
What I changed is receiving the props immediately and using it, instead of receiving it in state or componentWillMount:
export default class BarChart extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentWillMount() { }
componentDidMount() { }
render() {
return (
<div className="barChart">
<Bar
data={this.props.data}
/>
</div>
)
}
}
So, whenever the props is changed, the component will re-render.
I am trying to get an item "icon" from "weather" form following JSON
{
"coord": {
"lon": 14.33,
"lat": 49.94
},
"weather": [{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}]
}
I can't figure out how to exctract an item which is in array through render method.
My code is:
class Weather extends React.Component {
constructor() {
super();
this.state = {
'items': []
}
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch('http://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4')
.then(results => results.json())
.then(results => this.setState ({'items': results}));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>
})}
</div>
);
}
}
I actually used this question here: Get access to array in JSON by ReactJS ...which got me this far, but still can't make it working...
Your weather array is not set until your fetch request is complete, so this.state.items.weather.map in your render method will result in an error.
You could give weather an empty array as default value.
class Weather extends React.Component {
constructor() {
super();
this.state = {
items: {
weather: []
}
};
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch(
"http://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4"
)
.then(results => results.json())
.then(results => this.setState({ items: results }));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>;
})}
</div>
);
}
}
copy paste this example in codesandbox.io .you were initializing the items in constructor as array(where as fetch gave you an object) and for the initial render, items.weather was undefined and in render method you were trying to access map of undefined which was causing the error. (I have changed the url to https to run it in codesandbox)
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = {
items: {}
};
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch(
"https://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4"
)
.then(results => results.json())
.then(results => this.setState({ items: results }));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather &&
this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>;
})}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I have a React component render method defined as below, which includes passing a prop called onExchangeSelect into the ExchangeList component.
render() {
return (
<div className="ExchangeContainer list-group">
<ExchangeList
exchanges={this.state.exchanges} selected={this.state.selectedExchange}
onExchangeSelect={selectedExchange => this.setState({selectedExchange})}
/>
<ExchangeDetail exchange={this.state.selectedExchange} />
</div>
);
}
Then, in the ExchangeList constructor, when I console.log this.props, there is not a prop called onExchangeSelect which I can call and th.
The intent is to pass a callback function from the top level component to a child component, to be called by the child so as to affect the state of the parent component. The entire top-level class is below:
class ExchangeContainer extends Component {
constructor(props) {
super(props);
this.state = {
exchanges:[
{
name:"binance",
url:"https://bittrex.com"
},
{
name:"bittrex",
url:"https://bittrex.com"
}
],
selectedExchange:"binance"
};
}
render() {
return (
<div className="ExchangeContainer list-group">
<ExchangeList
exchanges={this.state.exchanges} selected={this.state.selectedExchange}
onExchangeSelect={selectedExchange => this.setState({selectedExchange})}
/>
<ExchangeDetail exchange={this.state.selectedExchange} />
</div>
);
}
}
Why is the function not available as a prop in the child component? (below):
class ExchangeList extends Component {
constructor(props) {
super(props);
this.state = {
};
console.log('This props ' + JSON.stringify(this.props))
}
render() {
console.log("EL: " + JSON.stringify(this.props))
const ExItemList = this.props.exchanges.map((exchange) => {
return <ExchangeListItem key={exchange.name} exchange={exchange}
onExchangeSelect={this.props.onExchangeSelect}/>
});
return (
<ul className="col-md-4 list-group bg-light" >
{ExItemList}
</ul>
);
}
}
i would inspect them in dev tools instead of console.log..place break point and check in chrome dev tool.. onExchangeSelect should be available as part of props in child component..
the offical docs says you should bind the method to a property inside the constructor function. you can play around on my codesandbox for the code below
constructor(props) {
super(props);
this.state = {
exchanges: [
{
name: "binance",
url: "https://bittrex.com"
},
{
name: "bittrex",
url: "https://bittrex.com"
}
],
selectedExchange: "binance"
};
// bind "this" to handleOnExchange method
this.handleOnExchange = this.handleOnExchange.bind(this);
}
// method to be bound
handleOnExchange (data) {
this.setState({selectedExchange: data})
}
render() {
const ExchangeList = props => <div />;
const ExchangeDetail = props => <div />;
return (
<div className="ExchangeContainer list-group">
<ExchangeList
exchanges={this.state.exchanges}
selected={this.state.selectedExchange}
// pass the method to a child property (onExchangeSelect)
onExchangeSelect={this.handleOnExchange}
/>
<ExchangeDetail selectedExchange={this.state.selectedExchange} />
</div>
);
}
to use it inside a (class-based) child component, call the method with an arg like this:
this.props.onExchangeSelect(arg)
The reason it can't see it is because you are looking for it in the wrong place. You are looping through the "exchange" props to create a new component so when you reference "this.props.onExchangeSelect", you are not referring the the props passed to the class as you expected but to the exchange object through which you are looping.
To remedy this, consider rewriting the ExchangeContainer component like so:
class ExchangeContainer extends Component {
constructor(props) {
super(props);
this.state = {
exchanges:[
{
name:"binance",
url:"https://bittrex.com"
},
{
name:"bittrex",
url:"https://bittrex.com"
}
],
selectedExchange:"binance"
};
}
setSelectedExchange = (selectedExchange) =>{
this.setState({selectedExchange})
};
render() {
return (
<div className="ExchangeContainer list-group">
<ExchangeList
exchanges={this.state.exchanges} selected={this.state.selectedExchange}
onExchangeSelect={selectedExchange => setSelectedExchange(selectedExchange)}
/>
<ExchangeDetail exchange={this.state.selectedExchange} />
</div>
);
}
}
And the ExchangeList component like so:
class ExchangeList extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
console.log("EL: " + JSON.stringify(this.props));
const {exchanges, selected, onExchangeSelect} = this.props;
const ExItemList = exchanges.map((exchange) => {
return <ExchangeListItem key={exchange.name} exchange={exchange}
onExchangeSelect={onExchangeSelect}/>
});
return (
<ul className="col-md-4 list-group bg-light" >
{ExItemList}
</ul>
);
}
}