Why am I unable to access the data property on this object? - javascript

I am working on my first React project and in this project I'm storing images on MongoDB. (Not best practice, I know, but regardless...)
My mongoDB object has a number of properties, including one property called "coverImage" which is an object that holds two properties: type & data, which looks like this: {Type: buffer, data: Array(6450).
I'm having trouble accessing the data property. When logging to the console the value of the data property within axios, the buffer is logged to the console without issue.
Meanwhile, when attempting to access this value, which was stored in state, I receive a TypeError: "Cannot read property data of undefined"
What am I doing wrong?
Please see my code below:
import React, { Component } from "react";
import axios from "axios";
// import BookCover from "../utilities/BookCover";
class BookPage extends Component {
constructor(props) {
super(props);
this.state = {
book: ""
};
}
componentDidMount() {
axios
.get("http://localhost:3000/books/" + this.props.match.params.id)
.then(res => {
console.log("first object", res.data.coverImage); // Returns fist object, {type: Buffer, data: Array(6450)}
console.log("buffer", res.data.coverImage.data); // Returns buffer (6450) [255, 216 ...]
this.setState({
book: res.data
});
});
}
bookDetails() {
console.log("second object", this.state.book.coverImage); // Returns second object, {type: Buffer, data: Array(6450)}
// console.log(this.state.book.coverImage.data); // Returns TypeError: "Cannot read property data of undefined"
return (
<div>
<h1>{this.state.book.title}</h1>
<p>Author: TBD</p>
<p>Publish Date: {this.state.book.publishDate} </p>
<p>Need to Read: {String(this.state.book.unread)} </p>
<p>Page Count: {this.state.book.pageCount} </p>
<p>Description: {this.state.book.description} </p>
</div>
);
}
render() {
return (
<div>
<div>{this.bookDetails()}</div>
</div>
);
}
}
export default BookPage;

Related

Displaying json arrays in React

I'm using React to display data,and I want to display data inside hits, which is array of recipes.Below is the structure. I want to list the properties inside each recipe.
recipe": {
"uri": "http://www.edamam.com/ontologies/edamam.owl#recipe_d090689494d5bc05e53e4a808bf6db1c",
"label": "Recipe For (A Kind Of) Pisto, With Rice And Eggs",
"image": "https://edamam-product-images.s3.amazonaws.com/web-img/b7c/b7c8284b065e055f74c59f3f88718b84.jpg?X-Amz-Security-Token=IQoJb3JpZ2luX2VjEPH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQDpnaXhSzGfXCmxvypbyEuHsQoaEVX9tsSxAHEAttR%2FuAIhAI%2Bso6mALweGSGbfFqjijfLZYKlNjx9vofkJPPR3o4P9KtUECPr%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMMTg3MDE3MTUwOTg2Igw%2FjCzJyZGmSs%2BA5uQqqQT3216RtVYOtYQ68ewLqgYN3afBsemEChcI0wO9St%2Fvk67aOwBGyJGwiaQCoLNaqIGobQXfkrtHjRPQVqNcTqtsRS4Qi%2B0tSSc%2BpodO5fpBejLyaUsEtP9REXSNjWdGSS%2BucN7yi5ObnF5O6DkWDxYVvA4vbisr%2FNkA2A7qPIyALSH7l7DQkvJDsMj%2BaKBxM3Ct3c019gbNplPbqL6XfG2iUPyUqGX0roUhOc2dcRLJWEh2urg6%2Fnla97k5hCUCV437I4vlT622Zr%2FpDpy%2FvnEwUNidqbS7B3u4oGmTNbQMGSx6h5TjmHIHqDoQzPnfsWs62GxYglzS55d%2BJhtL2tldl3y2iR%2FlDMXT1ejlz5YpU2dYpJzYKiM%2FhzHU8h8dJcTKoqmjjKMtMsp3XEfdfC7JNN5S%2BKQQdgIAjL%2B0n4aKWkPmddsP3pHi4bHGEbDrc2GUUUjHJMUityTKPb1sEieg0%2F%2FlLrhoi4Mj1%2BudlaT4Vmor9S4EIxnnRIDq5V0o1rUsLj784%2BfLBReZTCPa%2BOxT0ZpF%2FJyl9dZfakqCEXSdjWjzX07b08uTzYJIfyfwKHWrzW32wJ4aRUR7WdCcJ3n0rmjgpqKX6ozOeS8R5TP7HFeyVQcWcCyzmKDCSV40y761TasY%2BV5sqlURzjBL8%2F2iyztTydDhjopyQ1PBiewJeA06oRY4Ql3WiEySmRI4GwHOzyJOy7GY%2Fr27ZuVRolbX%2BP%2BworkCZpLrMMLAhZwGOqgBSIfkg1IG0s7EX17nVG89NMfvCCN4WXfempWOHkP2qAZtXrZOHyumjT4DUY2rDbbcqmVYBKAysjFa9NE%2BceP7%2BBNXLBBh4%2FMmaqvjZP1DYaRAj1ldEZr7E%2BNuLrADKB0Vb2pWv7mLYMcTzXzcOOLeabpSMPMsPyLNqkOr%2BperyDw6pSGebOajGpQA0QTV1m4z3HijmRicL2LLW4q5RooyF2oR5QKBu8%2Fk&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20221126T013542Z&X-Amz-SignedHeaders=host&X-Amz-Expires=3600&X-Amz-Credential=ASIASXCYXIIFP4CCVRCC%2F20221126%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=68656c34e850742a7b45117e905bbb71babec459015201e4246e65a5d6946d3b",
"source": "Mostly Eating",
"url": "http://www.mostlyeating.com/in-praise-of-pisto-and-a-perfectly-balanced-meal",
"shareAs": "http://www.edamam.com/recipe/recipe-for-a-kind-of-pisto-with-rice-and-eggs-d090689494d5bc05e53e4a808bf6db1c/tomato%2Cegg%2Cspinach",
"yield": 2.0,
"dietLabels": [
"Balanced",
"High-Fiber"
],
"healthLabels": [
"Vegetarian",
"Pescatarian",
"Dairy-Free",
"Gluten-Free",
"Wheat-Free",
"Peanut-Free",
"Tree-Nut-Free",
"Soy-Free",
"Fish-Free",
"Shellfish-Free",
"Pork-Free",
"Red-Meat-Free",
"Crustacean-Free",
"Celery-Free",
"Mustard-Free",
"Sesame-Free",
"Lupine-Free",
"Mollusk-Free",
"Alcohol-Free",
"Kosher"
],
"cautions": [ ]
}
Here is my code.
import React, { Component } from 'react';
import axios from 'axios'
import CallAPI from './CallAPI';
const api = 'http://localhost:5000' // backend address
class GetRecipe extends Component{
constructor(props){
super(props)
this.state={
list:[]
}
this.setState=this.setState.bind(this)
}
getData=()=>{
var url = `${api}/getRecipe`;
axios.get(url)
.then(response=>{
console.log(response.data)
this.setState({list:response.hits})
}).catch(err=>console.log(err))
}
render(){
return(
<div>
<h2>Get Recipe</h2>
<button onClick={this.getData}>Get</button>
<p>{this.state.list}</p>
</div>
)
}
}
export default GetRecipe;
Using the above code, there is no error, but nothing display in the page. So I tried another way:
<ul>
{this.state.list.map((value, key)=>{
return<li key={key}>{value}</li>
})}
</ul>
However, in this way I got error
getRecipe.js:37 Uncaught TypeError: Cannot read properties of undefined (reading 'map')
at GetRecipe.render (getRecipe.js:37:1)
I also try to change the getData function, then I got TypeError: Cannot read properties of undefined (reading 'setState').
getData(){
axios.get(`${api}/getRecipe`)
.then((response)=>{
this.setState({list:response.data})
})
.catch(function (error){
console.log(error)
})
}
I try this way of displaying dat before, but I don't know why it doesn't work this time. How can i make it work ?
In your first try, you have this line:
this.setState({list:response.hits})
This should (probably) be:
this.setState({list:response.data.hits})
This resolves your (first) TypeError.
Then you try to render it like this:
<p>{this.state.list}</p>
What is React supposed to do with this? It won't magically create HTML elements for your array.
Mapping over the array is the correct way to render an element per item in the array. But:
return<li key={key}>{value}</li>
value is a plain object. How is React supposed to render it? And consider using a real key, not just the array index.
Your change of getData() makes it forget this. You probably meant to bind it in the constructor instead of setState(). This resolves your second TypeError. Or change it back to an arrow function.

Vue prop default value not working properly

Ok so I have the following prop that I get from the parent component
props: {
selectedExchange: {
default: 'acx',
}
},
And i try to use it in the following method
methods: {
getMarkets() {
const ccxt = require('ccxt')
const exchanges = ccxt.exchanges;
let marketPair = new ccxt[this.selectedExchange]()
let markets = marketPair.load_markets()
return markets
}
},
The expected result should be an array of markets for my project but i get an error in the console
[Vue warn]: Error in mounted hook: "TypeError: ccxt[this.selectedExchange] is not a constructor"
Now i thought it might be a problem with ccxt but it's not! I have tried the following code
methods: {
getMarkets() {
const ccxt = require('ccxt')
const exchanges = ccxt.exchanges;
let acx = 'acx'
let marketPair = new ccxt[acx]()
let markets = marketPair.load_markets()
return markets
}
},
If you don't see the change I have made a variable that contains 'acx' inside, exactly the same like the prop but this time it's created inside the method, and with this code I get the expected result, It has been bugging me for days and I can't seem to find an answer to it, did I initialize the default value wrong? When i look inside vue dev tools the value for my prop is array[0], only after I pass a value to that prop it gets updated, shouldn't i see the default value of acx in devtools? Any help is much appreciated!
Edit 1: Added parent component code
This is how i use the methods inside the parent and how my components are related to each other,
<div id="exchange">
<exchange v-on:returnExchange="updateExchange($event)"></exchange>
</div>
<div id="pair">
<pair :selectedExchange="this.selectedExchange"></pair>
</div>
And this is the code inside the script tags, i didn't include the import tag cause i don't think it would be useful
export default {
name: 'App',
components: { exchange, pair, trades },
data(){
return{
selectedExchange: ''
}
},
methods: {
updateExchange(updatedExchange){
this.selectedExchange = updatedExchange
}
},
};
In this case you will inherit the default value:
<pair></pair>
In this case you will always inherit the value of selectedExchange, even if it's null or undefined:
<pair :selectedExchange="this.selectedExchange"></pair>
So, in your case, you have to handle the default value on parent component.
This should work:
export default {
name: 'App',
components: { exchange, pair, trades },
data(){
return{
selectedExchange: 'acx' // default value
}
},
methods: {
updateExchange(updatedExchange){
this.selectedExchange = updatedExchange
}
},
};

Browser error says "Cannot add property *, object is not extensible" - Even though property does exist in the class definition

I using React w/Sails.js backend. I think this question really only pertains to React/javascript.
I am getting the following browser console error even though the property being changed does exist in the object:
Cannot add property *, object is not extensible
Below is the class definition and the error occurs in the use of:
onExchangeSelect={selectedExchange => this.setState({selectedExchange})
in the render function below. That is, the browser complains the object is not extensible and gives that lines as the reason (and when I remove the onExchangeSelect function the error goes away).
class ExchangeContainer extends Component {
constructor(props) {
super(props);
this.state = {
exchanges:[
{
name:"binance",
url:"https://bittrex.com"
},
{
name:"bittrex",
url:"https://bittrex.com"
}
],
selectedExchange:null
};
}
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>
);
}
}
My question, what might be causing the error and subsequent failure for the React component to render? Why can't I change that property?
Try to spread/copy the object (otherwise, it is just using same memory location).
onExchangeSelect={selectedExchange =>
this.setState({
selectedExchange: {...selectedExchange}
})
}

How can I pass an object from container to class in Meteor and React?

I'm trying to get a value in an object of javascript but it fails somehow. I managed to get an intended data from mongoDB by findOne method. Here is my code and console log.
const title = Questions.findOne({_id: props.match.params.id});
console.log(title);
Then console says:
Object {_id: "bpMgRnZxh5L4rQjP9", text: "Do you like apple?"}
What I wanna get is only the text in the object. I have already tried these.
console.log(title.text);
console.log(title[text]);
console.log(title["text"]);
console.log(title[0].text);
But I couldn't access to it... The error message is below.
Uncaught TypeError: Cannot read property 'text' of undefined
It sounds super easy but I couldn't solve by my self. Could anyone help me out?
Additional Context
I'm using Meteor and React. I would like to pass the text inside of the object from the container to the class. I would like to render the text in render(). But it doesn't receive any data from the container... The console.log in the container works well and shows the object.
import React, { Component } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import { Questions } from '../../api/questions.js';
import PropTypes from 'prop-types';
import { Answers } from '../../api/answers.js';
import ReactDOM from 'react-dom';
import { Chart } from 'react-google-charts';
class MapClass extends React.Component{
handleAlternate(event){
event.preventDefault();
const country = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
Answers.insert({
country,
yes: false,
question_id:this.props.match._id,
createdAt: new Date(), // current time
});
ReactDOM.findDOMNode(this.refs.textInput).value = '';
}
handleSubmit(event) {
event.preventDefault();
const country = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
Answers.insert({
country,
yes: true,
question_id: this.props.match.params.id,
createdAt: new Date(), // current time
});
ReactDOM.findDOMNode(this.refs.textInput).value = '';
}
constructor(props) {
super(props);
this.state = {
options: {
title: 'Age vs. Weight comparison',
},
data: [
['Country', 'Popularity'],
['South America', 12],
['Canada', 5.5],
['France', 14],
['Russia', 5],
['Australia', 3.5],
],
};
this.state.data.push(['China', 40]);
}
render() {
return (
<div>
<h1>{this.props.title.text}</h1>
<Chart
chartType="GeoChart"
data={this.state.data}
options={this.state.options}
graph_id="ScatterChart"
width="900px"
height="400px"
legend_toggle
/>
<form className="new-task" onSubmit={this.handleSubmit.bind(this)} >
<input
type="text"
ref="textInput"
placeholder="Type to add new tasks"
/>
<button type="submit">Yes</button>
<button onClick={this.handleAlternate.bind(this)}>No</button>
</form>
</div>
);
}
}
export default MapContainer = createContainer(props => {
console.log(Questions.findOne({_id: props.match.params.id}));
return {
title: Questions.findOne({_id: props.match.params.id})
};
}, MapClass);
The problem here is that at the moment when the container is mounted, the data is not yet available. Since I do not see any subscriptions in your container I assume that you handle that elsewhere and thus there is no way of knowing when the data is ready. You have 2 options.
1) move the subscription into the container and use the subscription handle ready() function to assess if the data is ready. Show a spinner or something while it is not. Read this.
2) use lodash/get function (docs) to handle empty props. You would need to
npm install --save lodash
and then
import get from 'lodash/get';
and then in your class render method:
render() {
const text = get(this.props, 'title.text', 'you-can-even-define-a-default-value-here');
// then use `text` in your h1.
return (...);
}
Does this work for you?

Error in render function: "TypeError: Cannot read property of undefined" in Vue

I am using Laravel and vue-router.
<template>
<div class="content__inner">
<div class="forums">
<!-- Heading -->
<div class="forums__heading" :style="'border-bottom:2px solid #' + board.category.color">
<div class="lg-8 md-8 sm-12 column column__first">
<h2 class="forums__heading__title">{{ board.title }}</h2>
</div>
<div class="lg-1 md-1 sm-1 dtop column text-center">
<strong>Replies</strong>
</div>
<div class="lg-3 md-3 sm-4 column text-right">
<strong>Latest Reply</strong>
</div>
<div class="clearfix"></div>
</div>
<!-- Content -->
<div class="forums__content">
{{ board.category }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
board: [],
}
},
created() {
this.fetch_board(this.$route.params.slug);
},
methods: {
/**
* Fetch the board.
*
* #param string slug The slug for the board.
*/
fetch_board(slug)
{
this.$http.get('/api/forums/board/' + slug).then((response) => {
this.board = response.data;
});
},
}
};
</script>
The 'fetch_board' function returns an object like the following:
board:Object {
id:5,
title:"Game Discussion",
slug:"5-game-discussion",
description:"General talk about the game.",
restriction:null,
category_id:2,
category:Object {
id:2
title:"Community",
color:"2ECC71",
created_at:"2017-05-02 07:30:25",
updated_at:"2017-05-02 07:30:25",
}
created_at:"2017-05-02 07:30:25",
updated_at:"2017-05-02 07:30:25",
}
When I access the {{ board.category }} it displays the object correctly; but when I access {{ board.category.title }} it displays the title, but ALSO gives a TypeError.
Why I am getting this error if the data is being loaded correctly?
How can I avoid/fix this error?
You are seeing this error because you are initializing "board" to an empty array. The component tries to evaluate "board.category.title" when it binds the reactivity just prior to the created() hook.
With board set as an empty array, step by step the evaluation might look like this:
const board = [];
const category = board.category; // undefined
const title = category.title; // TypeError, because category is undefined
You should stop seeing this error if you initialize your data like so:
data() {
return {
board: {
category: {
title: ''
}
}
}
}
Here is the Vue lifecycle diagram which illustrates when the created() event is fired
This error is explained in the official Vue documentation:
Since Vue doesn’t allow dynamically adding root-level reactive properties, you have to initialize Vue instances by declaring all root-level reactive data properties upfront, even with an empty value:
var vm = new Vue({
data: {
// declare message with an empty value
message: ''
},
template: '<div>{{ message }}</div>'
})
// set `message` later
vm.message = 'Hello!'
If you don’t declare message in the data option, Vue will warn you that the render function is trying to access a property that doesn’t exist.

Categories