import React, { Component } from 'react';
class newsList extends React.Component {
render(){
return(
<div>
{JSON.stringify(this.props.arr)}
</div>
)
}
}
export default newsList;
In the above code, arr is an object coming from another component. I can display the data using JSON.stringify(this.props.arr.result). But as soon as I change it with JSON.stringify(this.props.arr.result.id), I am getting an error says TypeError: this.props.arr.result is undefined. I cannot understand what I am doing wrong here?
I'm almost positive that, at some point in time, your this.props.arr is undefined, but then eventually gets assigned a value. Your initial render will receive a null or undefined, but if you try and go one step further into a key that doesn't exist, you will throw that error. You can use a boolean to control what gets initially rendered.
Instead of this line of code:
{JSON.stringify(this.props.arr)}
try this:
{this.props.arr ? JSON.stringify(this.props.arr) : null}
edit: is your issue with this.props.arr.result.id? If so, use this instead
{this.props.arr.result ? JSON.stringify(this.props.arr.result.id) : null}
Is this.props.arr Array?
If it is, the render function should be
render(){
var ids = this.props.arr.map(e=>(<div>e.result.id</div>))
return(
<div>
{ids}
</div>
)
}
Try out this code instead:
class NewsList extends React.Component {
constructor(props) {
super(props);
this.props = props;
}
render() {
return <div>{this.props.arr}</div>;
}
}
A React.Component's constructor always receives props as it's first parameter.
Related
Recently I started to learn ReactJS with help of tutorials and I've run into an error I can't find solution for by myself. One of the first projects I decided to do is To-do list and when trying to pass "handleChange" function as a prop to my component I get this error TypeError: Cannot read property 'handleChange' of undefined.
Here is my full code of App class so you can see what I'm trying to do:
import React from 'react';
import './App.css';
import Content from "./Content"
import ToDoItems from "./ToDoItems"
class App extends React.Component {
constructor() {
super()
this.state = {
items: ToDoItems
}
this.handleChange = this.handleChange.bind(this)
}
handleChange() {
console.log("Change!")
}
render() {
const items = this.state.items.map(function(item){
return (<Content key={item.id} todo={item.item} checked={item.completed} handleChange={this.handleChange}/>)
})
return (
<div>
{items}
</div>
)
}
}
export default App;
I'm getting my data from file called ToDoItems and trying to pass them as props into Content component. Everything is working fine until I try to pass the function handleChange().
I must be doing something wrong. Any help would be appreciated.
The problem is here,
const items = this.state.items.map(function(item){
return (<Content key={item.id} todo={item.item} checked={item.completed} handleChange={this.handleChange}/>)
})
When ur using non-array function it binds this and others stuff to its own protoype. Meaning ur this.handleChanges this actually refers to function, instead of class
Try using this,
const items = this.state.items.map((item) => {
// ^^^^^^^^^^^
return (<Content key={item.id} todo={item.item} checked={item.completed} handleChange={this.handleChange}/>)
})
as Arrow functions don't bind this or any other stuff. so this should now refer to the class
// ClientDisplay.js
class ClientDisplay extends Component {
...
render() {
const {activeClient} = this.props
console.log(activeClient); // This works fine
const clientGroupMenu = (
<div>
<SelectMenu
defaultValue={activeClient.groupName}
...
)
return (
<div>{clientGroupMenu}</div>
)
}
}
export default ClientDisplay
// View.js
export class View extends Component {
...
render(){
return (
<ClientDisplay
{...this.props}
/>
)
}
...
}
As you can see the line console.log(activeClient) logs the correct data.
However I get an error on defaultValue={activeClient.groupName}
index.es.js:1 Uncaught TypeError: Cannot read property 'props' of null
I also get the same error if I use defaultValue={this.props.activeClient.groupName}
Does anyone know why I cannot access these properties from within this component clientGroupMenu.
Thanks
Moving ClientGroupMenu into a stateless functional component might help.
const ClientGroupMenu = ({activeClient}) => {
const {groupName} = activeClient
return (
<div>
<SelectMenu
defaultValue={groupName}
...
)
}
class ClientDisplay extends Component {
...
render() {
const {activeClient} = this.props
console.log(activeClient); // This works fine
return (
<div><ClientGroupMenu activeClient={activeClient} /></div>
)
}
}
Here is sample format like below you should use super() inside constructor :
class ClientDisplay extends Component {
constructor(props) {
super(props);
}
[...]
}
If you are getting same error then Use self.props instead of this.props everywhere!
Issue is probably because of destructuring assignment.
Use const activeClient declaration instead of const {activeClient}
Refer https://itnext.io/using-es6-to-destructure-nested-objects-in-javascript-avoid-undefined-errors-that-break-your-code-612ae67913e9 for more details about destructuring assignment.
When const {activeClient} = this.props is used, the actual object's value is copied within activeClient. You have to use defaultValue={activeClient} instead of defaultValue={activeClient.groupName}
When const activeClient = this.props is used,this.props is copied within activeClient. You have to use defaultValue={activeClient.groupName} in that case.
The following is the state set:
State to be passed
which I pass in the SearchResults Component:
SearchResults isRemoval='false' onAdd={this.addTrack}
searchResults={this.state.searchResults}/>
(im aware that the beginning is missing the < symbol I had to leave it out because it was hiding it with the < not omitted)
and then in SearchResults I pass the props through to
class SearchResults extends React.Component {
render() {
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList isRemoval={this.props.isRemoval} onAdd={this.props.onAdd}
tracks={this.props.searchResults}/>
</div>
)
}
}
And then in Tracklist I try to use map on the array but I get an error saying it cant perform map on undefined.
class TrackList extends React.Component {
render() {
return (<div className="TrackList">
{
this.props.tracks.map(track => {
return <Track track={track} />
})
}
</div>
);
}
}
When I call the props in the first component down it is defined and I can use the array as desired but when passed to the second component it shows up undefined in console and I can not use map. Does any one know why this is happening?
So the problem was I was passing props to the same component from another component that had an error in it. I had forgotten that I was passing props from more than one source thats why I was getting the undefined error. Thanks everyone for your help.
Are you loading that data through something external, and if so does your state have a default value? If you're not setting searchResults as an empty array, initially you won't have a value for searchResults.
If that's not the case, there must be some part of the program we're missing here, because the code seems fine.
Try logging each component's entire props every step of the way, you'll get a better idea of when it gets lost.
This is an interesting issue you're facing, but I would suggest that you first check if this.props.tracks is null || undefined before trying to map through it.
If my hypothesis is correct, when the render method for the TrackList component initially runs, this.props.tracks is undefined, therefore it probably throws an error.
The following code could possibly solve your issue:
class TrackList extends React.Component {
render() {
return(
const { tracks } = this.props;
<div className="TrackList">
{tracks ? tracks.map(track => {
return <Track track={track} />;
}) : null}
</div>
);
}
}
Child component updates when its state or the parent's state changes but not if parent's props changes.
Create state in SearchResults and pass that state to child component
class SearchResults extends React.Component {
constructor(){
super();
this.state = {
tracks: []
}
}
componentWillReceiveProps(newProps){
if(newProps.searchResults){
this.setState({
tracks: newProps.searchResults
});
}
}
render() {
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList isRemoval={this.props.isRemoval} onAdd={this.props.onAdd}
tracks={this.state.tracks}/>
</div>
)
}
}
Same Applies to other props too..
Hope this helps
I am having a bit of an issue rendering components before the state is set to the data from a returned asynchronous API request. I have a fetch() method that fires off, returns data from an API, and then sets the state to this data. Here is that block of code that handles this:
class App extends Component {
constructor() {
super();
this.state = {
currentPrice: null,
};
}
componentDidMount() {
const getCurrentPrice = () => {
const url = 'https://api.coindesk.com/v1/bpi/currentprice.json';
fetch(url).then(data => data.json())
.then(currentPrice => {
this.setState = ({
currentPrice: currentPrice.bpi.USD.rate
})
console.log('API CALL', currentPrice.bpi.USD.rate);
}).catch((error) => {
console.log(error);
})
}
getCurrentPrice();
}
You will notice the console.log('API CALL', currentPrice.bpi.USD.rate) that I use to check if the API data is being returned, and it absolutely is. currentPrice.bpi.USD.rate returns an integer (2345.55 for example) right in the console as expected.
Great, so then I assumed that
this.setState = ({ currentPrice: currentPrice.bpi.USD.rate }) should set the state without an issue, since this data was received back successfully.
So I now render the components like so:
render() {
return (
<div>
<NavigationBar />
<PriceOverview data={this.state.currentPrice}/>
</div>
);
}
}
export default App;
With this, I was expecting to be able to access this data in my PriceOverview.js component like so: this.props.data
I have used console.log() to check this.props.data inside my PriceOverview.js component, and I am getting 'null' back as that is the default I set intially. The issue I am having is that the components render before the API fetch has ran it's course and updated the state with the returned data. So when App.js renders the PriceOverview.js component, it only passes currentPrice: null to it, because the asynchronous fetch() has not returned the data prior to rendering.
My confusion lies with this.setState. I have read that React will call render any time this.setState is called. So in my mind, once the fetch() request comes back, it calls this.setState and changes the state to the returned data. This in turn should cause a re-render and the new state data should be available. I would be lying if I didn't say I was confused here. I was assuming that once the fetch() returned, it would update the state with the requested data, and then that would trigger a re-render.
There has to be something obvious that I am missing here, but my inexperience leaves me alone.. cold.. in the dark throws of despair. I don't have an issue working with 'hard coded' data, as I can pass that around just fine without worry of when it returns. For example, if I set the state in App.js to this.state = { currentPrice: [254.55] }, then I can access it in PriceOverview.js via this.props.data with zero issue. It's the async API request that is getting me here, and I am afraid it has gotten the best of me tonight.
Here App.js in full:
import React, { Component } from 'react';
import './components/css/App.css';
import NavigationBar from './components/NavigationBar';
import PriceOverview from './components/PriceOverview';
class App extends Component {
constructor() {
super();
this.state = {
currentPrice: null,
};
}
componentDidMount() {
const getCurrentPrice = () => {
const url = 'https://api.coindesk.com/v1/bpi/currentprice.json';
fetch(url).then(data => data.json())
.then(currentPrice => {
this.setState = ({
currentPrice: currentPrice.bpi.USD.rate
})
console.log('API CALL', currentPrice.bpi);
}).catch((error) => {
console.log(error);
})
}
getCurrentPrice();
}
render() {
return (
<div>
<NavigationBar />
<PriceOverview data={this.state.currentPrice}/>
</div>
);
}
}
export default App;
Here is PriceOverview.js in full:
import React, { Component } from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
class PriceOverview extends Component {
constructor(props) {
super(props);
this.state = {
currentPrice: this.props.data
}
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.state.currentPrice != null ? <div className="price">{this.state.currentPrice}</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}
export default PriceOverview;
Thank you in advance to any help, it's much appreciated.
this.setState ({
currentPrice: currentPrice.bpi.USD.rate
})
Do not put an = in this.setState
Ok First thing, when you're writting code on React the components that hold state are the class base components so ... What I see here is that you're creating two class base components so when you pass down props from your app class component to your PriceOverview wich is another class base component you're essentially doing nothing... Because when your constructor on your PriceOverview get call you're creating a new state on that Component and the previous state ( that's is the one you want to pass down) is being overwritten and that's why you're seem null when you want to display it. So it should work if you just change your PriveOverview component to a function base component ( or a dumb component). So this way when you pass down the state via props, you're displaying the correct state inside of your div. This is how would look like.
import React from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
const PriceOverview = (data) => {
return (
<div className="overviewBar">
<div className="currentPrice panel">
//Im calling data here because that's the name you gave it as ref
//No need to use 'this.props' you only use that to pass down props
{data != null ? <div className="price">
{data}</div> : <div className="price">Loading...</div>
}
</div>
</div>
)
}
}
export default PriceOverview;
Whenever you're writing new components start always with function base components if you component is just returning markup in it and you need to pass some data go to his parent component update it (making the api calls there or setting the state there) and pass down the props you want to render via ref. Read the React docs as much as you can, hope this explanation was useful (my apologies in advance if you don't understand quite well 'cause of my grammar I've to work on that)
The thing is constructor of any JS class is called only once. It is the render method that is called whenever you call this.setState.
So basically you are setting currentPrice to null for once and all in constructor and then accessing it using state so it will always be null.
Better approch would be using props.
You can do something like this in your PriceOverview.js.
import React, { Component } from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
class PriceOverview extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.props.data!= null ? <div className="price">{this.props.data}</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}
export default PriceOverview;
Or you can use react lifecycle method componentWillReceiveProps to update the state of PriceOverview.js
componentWillReceiveProps(nextProps) {
this.setState({
currentPrice:nextProps.data
});
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.state.currentPrice != null ? <div className="price">{this.state.currentPrice }</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}
Render does indeed get called and even though the debugger shows that temp is populated properly, the change doesnt seem to take place (the json string doesnt make it to the dom). I am probably missing something obvious.
class ProfileComponent extends Component {
constructor(props){
this.props = props;
}
componentDidMount(){
window.mainStore.subscribe(this.render.bind(this))
}
render() {
var temp = JSON.stringify(window.mainStore.getState().profile);
return (
<div>
{temp}
</div>
);
}
}
Debugging looks like :
It seems that the first time 'ProfileComponent' rendered we don't have the subscribe method, after the componentDidMount we see the correct result , lets try to add a state which blocks the first render to return invalid {temp}:
class ProfileComponent extends Component {
constructor(props){
super(props);
this.state = { loading: true, temp:''};
}
componentDidMount() {
window.mainStore.subscribe(); // I don't see all your code but i think here we don't need to bind the render because the component will render after again after changing the state (you can try it both)
const temp = JSON.stringify(window.mainStore.getState().profile); // better keeping 'window' logic here (especially if our app is SSR)
this.setState({loading: false,temp});
}
render() {
const {loading, temp} = this.state;
if(loading) {return (<div>Loading...</div>)}
return (
<div>
{temp}
</div>
);
}
};