Trouble mapping over data - Cannot read property 'documents' of undefined - reactjs

I am saving some data (3 documents) into state.
I am receiving 'Uncaught TypeError: Cannot read property 'documents' of undefined' error when trying to map over an array in my react app.
Can anybody see where I am going wrong? 3 items should be mapped in the render function.
export default class Kim extends React.Component<IKimProps, IKimState> {
public constructor(props) {
super(props);
this.state = {
documents: []
}
//SPComponentLoader.loadCss('//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css');
}
componentDidMount() {
axios
.get("https://hiddenforsecurityreasons.com/sites/KIM/_api/lists/getbytitle('MyList')/items?$select=Id,Title,File/ServerRelativeUrl&$expand=File",
{ params:{},
headers: { 'accept': 'application/json;odata=verbose' }
})
.then(response => {
this.setState({
documents: response.data
})
})
.catch(error => {
console.log(error);
});
}
public render(): React.ReactElement<IKimProps> {
return (
<div className={ styles.kim }>
{ setTimeout(function(){
this.state.documents.d.results.map(document => {
<li>{document.Title}</li>
})
}, 6000) }
</div>
);
}
}
Thanks for looking!

When inside the timeout function, this is a reference to that function scope, not your React class scope, so this.state inside the timeout function is actually undefined because there's no state inside the function, so when your code tries to read documents, it can't because you cannot get documents from undefined, as it states.
You can remove your timeout and add an if check around your map call instead.
public render(): React.ReactElement<IKimProps> {
return (
<div className={ styles.kim }>
{this.state.documents.length? this.state.documents.d.results.map(document => <li>{document.Title}</li> ) : null }
</div>
);
}
Another way of dealing with this while keeping your timeout function is to use arrow functions.
While in ES5 ‘this’ referred to the parent of the function, in ES6,
arrow functions use lexical scoping — ‘this’ refers to it’s current
surrounding scope and no further. read more here

try arrow function, for sure it is a problem with this
{ setTimeout(() => {
this.state.documents.d.results.map(document => {
<li>{document.Title}</li>
})
}, 6000) }

Related

How to pass parameter to a onClick function in ReactJS

in my application, I defined a function click_button_response(param1) which writes the param1 to a DynamoDB table.
Then my app subscribes to an IoT Topic. It will receive a real-time message from a topic. When the button is clicked, the click_button_response(param1) function, passes the url as param1. I hope url can be saved in DynamoDB. However, it gave me the error message:
"Error Error: Pass options.removeUndefinedValues=true to remove undefined values from map/array/set."
I replaced the parameter with a string and the error went away, so I am confident that I passed the parameter in a wrong way. Thank you!
import putItemInDynamoDB from functions.js
const click_button_response = async (param1) => {
putItemInDynamoDB(param1)
}
class Sensors extends React.Component{
constructor(props) {
super(props);
this.state = {
sensorMsg: '{"null": 0}'
};
}
componentDidMount(){
Amplify.PubSub.subscribe(TOPIC).subscribe({
next: data => {
try{
this.setState({ sensorMsg: data.value });
}
catch (error){
console.log("Error, are you sending the correct data?");
}
},
error: error => console.error(error),
close: () => console.log('Done'),
});
}
render(){
const { sensorMsg } = this.state;
const url = sensorMsg['param1'];
return(
<div className="Sensor">
<button onClick={() => click_button_response({url})}>Button1</button>
</div>
)
}
}
if click_button_response expects param1 to be a string you are currently passing it an object. () => click_button_response({url}) when we enclose url in curly braces it actually gets converted into object { url: 'actual url value' }. Change the onClick handler function to this () => click_button_response(url). Also sensorMsg['param1'] is the output of this the expected url string?

Unhandled Rejection (TypeError): this is undefined

Getting this is undefined error. Tried searching for solution but didn't find any.
This same code is working fine on another page but don't know what's wrong here. Printing output of brands before setState is printing valid result.
class FindSponsors extends Component {
constructor() {
super();
this.state = {
data: []
}
}
componentDidMount() {
db.ref("sponsor").once("value").then(function (snapshot) {
let data = snapshot.val()
let brands = []
for (let brand in data) {
brands.push({
name: data[brand].Name,
website: data[brand].Website
})
}
this.setState({ //Error here
data: brands
})
});
}
render() {
return (
<div className="main">
</div>
);
}
}
export default FindSponsors;
Try using the arrow function:
db.ref("sponsor").once("value").then((snapshot) => { /*your function body here*/})
this keyword is nothing more than a link to an execution context. In your example above you "execute" function "in the callback".
Try reading this to understand better how it works and how arrows functions are different from regular ones.
The problem here is that when you define your callback function in the .then, the context of this function changes and this will basically refer to the inside of the function.
Two solutions here:
Use the .bind method to change the value of this for your callback. It would be something like this:
const callback = function (snapshot) {
// Your callback logic
}
db.ref("sponsor").once("value").then(callback.bind(this));
Use an arrow function. They have the specificity to not have their own bindings of the this keyword. This would be like this:
db.ref("sponsor").once("value").then((snapshot) => {
// Your callback logic
});

Cannot read property 'someFunction' of null for 'someFunction' within onClick

I am rendering a list of items retrieved from Firebase. For each item, I render a div, that includes a button that removes said item.
Relevant code:
constructor(props){
// Pass props to parent class
super(props);
this.removeItem = this.removeItem.bind(this);
this.getList = this.getList.bind(this);
...
}
removeItem(key) {
this.ref(key).remove()
}
getList() {
var list = []
this.ref.on("value", function (snapshot) {
for (var key in snapshot.val()) {
list.push(<div class="todo-item">{snapshot.val()[key]} <button onClick={() => this.removeItem(key)}> X </button> </div>)
}
}, function (error) {
console.log("Error: " + error.code);
});
return(list)
}
render() {
return (
<div class="todolist">
.....
{this.getList()}
</div>
)
}
The list of items and their remove buttons renders fine. However, when clicking the remove button, I get a TypeError: Cannot read property 'removeItem' of null
As removeItem is a function of this, I assume that this is not properly bound and thus null.
However, I bound both functions removeItem and getList in the constructor.
Does someone know where I am going wrong?
This is the most common problem of context being lost when this is accessed from anonymous function.
To get around this,
Use arrow functions:
this.ref.on("value", (snapshot) => { ... }, (error) => { ... });
OR
Use bind(this)
this.ref.on("value", function (snapshot) { ... }.bind(this), function (error) { ... }.bind(this));

Accessing nested json in React

I can successfully fetch json object from the api. However there seems to be a problem in updating the state.
After printing the object I get expected and desired result console.log(videos2.list[0]); gives 1st item from the list attribute of json object, you can check how the api looks under this link:
https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars
However when updating state with setState property selectedVideo: videos.list[0] is undefined.
The code for the component:
class App extends Component {
constructor(props) {
super(props)
this.state = {
videos2:[],
selectedVideo:null
}
this.DMSearch()
}
DMSearch(){
fetch(`https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars`)
.then(result => result.json())
.then(videos2 => {
console.log(videos2.list[0]);
this.setState({
videos2: videos2.list,
selectedVideo: videos2.list[0]
});
console.log(selectedVideo);
});
}
render () {
const DMSearch = this.DMSearch()
return (
<div>
<SearchBar onSearchTermChange= {DMSearch}/>
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo=>this.setState({selectedVideo})}
videos2={this.state.videos2}/>
</div>
)
}
}
And exact error is Uncaught (in promise) ReferenceError: selectedVideo is not defined
videos2:list[0] produces correct results, but when assigned to selectedVideo it is undefined
As requested I am also including the code for vide_list which uses the objects form parent component which might be producing error here:
const VideoList = (props) => {
const videoItems = props.videos.map((video)=>{
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.etag}
video={video} />
)
})
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
)
}
To be specific, this line
const videoItems = props.videos.map((video)=> {
causes the error when reading props. I believe this has something to do with selectedVideo being null...
There is an issue at this line
console.log(selectedVideo);
You printed out an undefined variable. It should be this.state.selectedVideo.
The error selectedVideo is not defined happens because selectedVideo is not a variable, it is a key on the javascript object passed to this.setState function.
//...
this.setState({
videos2: videos2.list,
selectedVideo: videos2.list[0]
});
//...
You can mitigate the error by creating a variable:
DMSearch(){
fetch(`https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars`)
.then(result => result.json())
.then(videos2 => {
console.log(videos2.list[0]);
const selectedVideo = videos2.list[0]; // create variable
this.setState({
videos2: videos2.list,
selectedVideo: selectedVideo
});
console.log(selectedVideo);
});
}

getting error: Cannot read property state of undefined

import React, { Component } from "react";
import FormUpdate from "../components/formUpdate";
import { fetchClothingItem, updateClothingItem } from "../actions/crud";
export default class Update extends Component {
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
}
componentWillMount() {
fetchClothingItem(this.props.match.params.postId)
.then(data => {
this.setState(state => {
state.updateClothingItem = data;
return state;
});
console.log("data", data);
//HERE IT IS RETURNING EXPECTED DATA
console.log("this.state.updateClothingItem",this.state.updateClothingItem)
})
.catch(err => {
console.error("err", err);
});
}
handleSubmit(data) {
//HERE IT IS THROWING:
> "TypeError: Cannot read property 'state' of undefined"
console.log("this.state.updateClothingItem", this.state.updateClothingItem);
updateClothingItem(this.state.updateClothingItem.id, data); this.props.router.push("/update");
}
render() {
return (
<div>
<FormUpdate
//onSubmit={this.handleSubmit.bind(this)}
id={this.state.updateClothingItem.id}
name={this.state.updateClothingItem.name}
sleeveLength={this.state.updateClothingItem.sleeveLength}
fabricWeight={this.state.updateClothingItem.fabricWeight}
mood={this.state.updateClothingItem.body}
color={this.state.updateClothingItem.color}
/>
<button
type="submit"
onClick={this.handleSubmit}
className="addItemButton"
>
Button
</button>
</div>
);
}
}
There are a few things that are technically wrong in terms of React code implementation.
Firstly, With ES6 style of writing a class, any function that needs to access the Class properties need to be explicitly binded. In your case you need to bind the handleSubmit function using arrow function of or binding in constructor.
See this answer for more details: Why and when do we need to bind functions and eventHandlers in React?
Secondly: You have your async request set up in the componentWillMount function and in the success response of it, you are setting state. However using setState in componentWillMount is triggered after the component is rendered so you still need to have an undefined check. You should instead make use of componentDidMount lifecycle function for async requests.
Check this answer on whether to have AJAX request in componentDidMount or componentWillMount
Third: setState is asynchronous and hence logging the state values after the setState function won't result in the correct output being displayed. Use the setState callback instead.
See these answers for more details:
calling setState doesn't mutate state immediately
When to use React setState callback
Code:
export default class Update extends Component {
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
}
componentDidMount() {
fetchClothingItem(this.props.match.params.postId)
.then(data => {
this.setState(state => {
state.updateClothingItem = data;
return state;
});
console.log("data", data);
//HERE IT IS RETURNING EXPECTED DATA
console.log("this.state.updateClothingItem",this.state.updateClothingItem)
}) // this statement will not show you correct result since setState is async
.catch(err => {
console.error("err", err);
});
}
handleSubmit = (data) => { . // binding using arrow function here
console.log("this.state.updateClothingItem", this.state.updateClothingItem);
updateClothingItem(this.state.updateClothingItem.id, data); this.props.router.push("/update");
}
render() {
return (
<div>
<FormUpdate
//onSubmit={this.handleSubmit.bind(this)}
id={this.state.updateClothingItem.id}
name={this.state.updateClothingItem.name}
sleeveLength={this.state.updateClothingItem.sleeveLength}
fabricWeight={this.state.updateClothingItem.fabricWeight}
mood={this.state.updateClothingItem.body}
color={this.state.updateClothingItem.color}
/>
<button
type="submit"
onClick={this.handleSubmit}
className="addItemButton"
>
Button
</button>
</div>
);
}
}
You forgot to bind your handleSubmit function to the class. You can either use arrow function to define the function.
handleSubmit=(data) =>{
...
}
Or you can bind the function in your constructor.
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
this.handleSubmit= this.handleSubmit.bind(this,data);
}
there is no state in constructor yet
if you want to set state in constructor you can do it like this
class SomeComponent extends Component {
constructor(props){
super(props)
this.state = { someKey: someValue }
}
}
or even like this
class SomeComponent extends Component {
state = { someKey: someValue }
}
but in this case babel should be properly configured

Resources