In my react file, I need to check if my data variable is undefined or not in my DisplayCharacterDetails() function. However, when the function runs, I do not get the data.
When I console.log(data.character), I get the following:
{__typename: "Character", id: "5f4be9d5652cd07eda934rt6", name: "Iron Man", studio: "Marvel", affiliation: "Avengers", …}
However, when I do data.name, nothing prints out.
const {loading, data} = useQuery(getSingleCharacterQuery, {
variables: {id: props.characterID}
});
function DisplayCharacterDetails() {
console.log(data.character)
if(data) {
return (
<div>
<h2>{data.name}</h2>
<p>{data.studio}</p>
<p>{data.affiliation}</p>
<p>{data.protrayedBy}</p>
<p>{data.creator.name}</p>
<p>All characters by this creator: </p>
<ul className="other-characters">
{ data.creator.characters.map(item =>{
return <li key={item.id}> {item.name}</li>
})
}
</ul>
</div>
)
}
else {
return (
<div>
No Character Selected
</div>
)
}
}
Looks like to get the character object:
Console.log(data.character);
To get the character name:
console.log(data.character.name);
Looks like name is a property of character according to your object...
Ok the problem was to check if data.character is null or not. So, we need 2 if statements like so:
if(data && data.character) {
return (
<div>
<h2>name: {data.character.name}</h2>
<p>{data.character.studio}</p>
<p>{data.character.affiliation}</p>
<p>{data.character.protrayedBy}</p>
<p>{data.character.creator.name}</p>
<p>All characters by this creator: </p>
{/* <ul className="other-characters">
{ data.creator.characters.map(item =>{
return <li key={item.id}> {item.name}</li>
})
}
</ul> */}
</div>
)
The error can be not only in data.name but also in <p>{data.creator.name}</p> or in <li key={item.id}> {item.name}</li>. You can use the https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining to prevent reading from undefined property.
const {loading, data} = useQuery(getSingleCharacterQuery, {
variables: {id: props.characterID}
});
function DisplayCharacterDetails() {
console.log(data.character)
if(data) {
return (
<div>
<h2>{data.name}</h2>
<p>{data.studio}</p>
<p>{data.affiliation}</p>
<p>{data.protrayedBy}</p>
<p>{data.creator?.name}</p>
<p>All characters by this creator: </p>
<ul className="other-characters">
{ data.creator.characters.map(item =>{
return <li key={item.id}> {item?.name}</li>
})
}
</ul>
</div>
)
}
else {
return (
<div>
No Character Selected
</div>
)
}
}
Related
I got 2 objects inside the array, and 1st object is longer than the 2nd object. How can i render all of the properties of the 1st object without getting undefined, i get undefined because there are only 2 properties existing in the second object of the array .Also how can i calculate total sum of exercises?
function App() {
const course = [
{
name: 'Half Stack application development',
id: 1,
parts: [
{
name: 'Fundamentals of React',
exercises: 10,
id: 1
},
{
name: 'Using props to pass data',
exercises: 7,
id: 2
},
{
name: 'State of a component',
exercises: 14,
id: 3
},
{
name: 'Redux',
exercises: 11,
id: 4
}
]
},
{
name: 'Node.js',
id: 2,
parts: [
{
name: 'Routing',
exercises: 3,
id: 1
},
{
name: 'Middlewares',
exercises: 7,
id: 2
}
]
}
]
// calculate total of exercises
const totalExercises = course.reduce((total, course) => total + course.exercises, 0);
return (
<div className="App">
<header className="App-header">
<h1>Seoul</h1>
<Course course={course} totalExercises={totalExercises} />
</header>
</div>
)
}
function Course({ course, totalExercises }) {
return (
<>
<ul>
{course.map((course) => (
<li key={course.id}>
<p>{course.name} {course.exercises}</p>
<p>{course.parts[0].name}</p>
<p>Total exercises: {course.parts[0].exercises},</p>
<p>{course.parts[1].name}</p>
<p>Total exercises: {course.parts[1].exercises}</p>
// Undefined one below
UNDEFINED <p>{course[0].parts[2].name}</p>
</li>
))}
</ul>
</>
);
}
You could use map the parts array to the elements:
function Course({ course, totalExercises }) {
return (
<>
<ul>
{course.map((course) => (
<li key={course.id}>
<p>{course.name} {course.exercises}</p>
{
course.parts.map((part, id)=>(
<React.Fragment key={id}
<p>{part.name}</p>
<p>Total Excercises: {part.exercises}</p>
</React.Fragment>
))
}
</li>
))}
</ul>
</>
);
}
If you are not sure that if a key is present in an object and want to render it if it is there without having any error, use ?. to access keys.
For example
let a ={name:'Shivansh'};
console.log(a?.name,a?.id);
a ={id:3};
console.log(a?.name,a?.id);
Output for 1st console.log
Shivansh undefined
2nd console.log
undefined 3
One more thing you can give a fallback customized text if you want instead of undefined by using ?? operator.
op1 ?? op2
if op1 gives undefined then op2 is executed
Ex->
console.log(a?.name??'',a?.id??'')
//This will ensure you don't receive undefined but empty string.
To calculate total sum of excercies->
let sum = 0;
course.forEach(course => course ? .parts ? .forEach(part => sum = sum + p
parseInt(part ? .exercises ? ? 0)))
function Course({ course, totalExercises }) {
return (
<>
<ul>
{course.map((course) => (
<li key={course.id}>
<p>{course.name} {course.exercises}</p>
{course.parts.map((part,i) => {
return(
<div key={i}>
<p>{part.name}</p>
<p>Total exercises: {part.exercises},</p>
</div>
)
})}
</li>
))}
</ul>
</>
);
}
Same way that you are mapping course.map(... you can then map the parts for each course, code above works without an error for me.
You try to render an array manually... it's a bad idea imagine that your array is dynamic how you can anticipate the number of elements in the array?
Done as follows.
function Course({ course, totalExercises }) {
return (
<>
<ul>
{course.map((course) => (
<li key={course.id}>
<p>{course.name} {course.exercises}</p>
{course.parts?.map((part, index) => (
<div key={index}>
<p>{part.name}</p>
<p>Total exercises: {part.exercises},</p>
</div>
))}
</li>
))}
</ul>
</>
);
}
I hope my English doesn't tire you, I'm French-speaking
I'm trying to add content into a component using json file where I require only the first component to have the cover image and rest of the components to ignore it.
Schema: "TrackList": [
{
"CourseNo": "1",
"CourseName": "C++ Programming with DataStructures",
"CoverImg":"example.com/cover.jpg"
},
{
"CourseNo": "2",
"CourseName": "Competitive Programming"
},
{
"CourseNo": "3",
"CourseName": "Java"
}
]
the below code renders as required but it also adds blank image into components except the first one.
render(){
const { Data } = this.props;
const Courses = Data.TrackList.map(course => {
return (
<li>
<span>Course {course.CourseNo}</span>
<a href='#path-10'>{course.CourseName}</a>
<img src={course.CoverImg}/>
</li>
)
});
return(
<div className='col-md-4 right-pannel'>
<ul>
{Courses}
</ul>
</div>
)
}
Array.prototype.map calls the provided callback function with the current array index (as the second argument). The first index of an array is 0, so you just need to add some logic that makes sure the image is only added when the index is equal to 0, like this:
const Courses = Data.TrackList.map((course, i) => {
return (
<li>
<span>Course {course.CourseNo}</span>
<a href='#path-10'>{course.CourseName}</a>
{ i === 0 && <img src={course.CoverImg}/> }
</li>
)
});
And here's a slightly modified version of your code turned into a runnable snippet:
const Data = {"TrackList": [{"CourseNo": "1","CourseName": "C++ Programming with DataStructures","CoverImg":"example.com/cover.jpg"},{"CourseNo": "2","CourseName": "Competitive Programming"},{"CourseNo": "3","CourseName": "Java"}]}
const Courses = Data.TrackList.map((course, i) =>
<li>
<span>CourseNo: {course.CourseNo}</span>
<span> | CourseName: {course.CourseName}</span>
{ i === 0 && <span> | CoverImg: {course.CoverImg}</span> }
</li>
);
ReactDOM.render(
<div className='col-md-4 right-pannel'>
<ul>
{Courses}
</ul>
</div>,
document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Or more generic solution can be the below code, as can be seen in your data, other object does not have CoverImg key.
const Courses = Data.TrackList.map(course => {
return (
<li>
<span>Course {course.CourseNo}</span>
<a href='#path-10'>{course.CourseName}</a>
{course.CoverImg && <img src={course.CoverImg}/>}
</li>
)
});
You can put condition with && to render only object which is having image
Optimized
-Unique key for every row
-&& condition for available image path
-another way removing return(){}
Code
render() {
const { Data } = this.props;
const Courses = Data.TrackList.map(course => (
<li key={course.CourseNo}>
<span>Course {course.CourseNo}</span>
{course.CourseName}
{course.CoverImg && <img src={course.CoverImg} alt="imgicon" />}
</li>
));
return (
<div className="col-md-4 right-pannel">
<ul>{Courses}</ul>
</div>
);
}
I have the bellow alike data, and I would like to render them.
Let's say I would like to display firstName, address, and seatType and flightId for each flight the passenger has.This has to be done for each passenger. How can I achieve that?
Updated
[
{
"id": 1,
"firstName": "Smith",
"lastName": "John",
"address": [
"1 Street",
"YYY",
],
"flights": [
{
"flightId": 1,
"seatType": "oridinary"
},
{
}
]
},
{},
]
Here is my code
render() {
const { data } = this.state;
return (
<div>
{" "}
{Object.keys(data).map((key, index) => (
<p key={index}>
{" "}
{key} {data[key].flights}
{data[key].flights.map(k => (
{data[key].flights[k]}
))}
</p>
))}
</div>
);
}
I'm assuming you're looking for something like this:
return (
<div>
{
passengers.map(passenger => {
if (!passenger.id) { return null } /* + */
return (
<div key={passenger.id}>
<span>{passenger.firstName} {passenger.lastName}</span>
<div>
<span>Passenger's Flights</span>
{
passenger.flights && /* + */
Array.isArray(passenger.flights) && /* + */ passenger.flights.map(flight => {
if (flight.flightId) {
return (
<div key={flight.flightId}>
{flight.seatType}
</div>
)
}
return null
})
}
</div>
</div>
)
})
}
</div>
);
}
Note: remember that you should not use index as a key.
Edit: You need to add a null/undefined check
render() {
const { data } = this.state;
return (
<div>
{data.map((passenger, index) => (
<p key={index}>
{passenger.firstName} {passenger.address.join(' ')}
{passenger.flights.map(flight => (
<p>{flight.seatType} {flight.flightId}</p>
))}
</p>
))}
</div>
);
}
render() {
const { data } = this.state;
return (
<div>
{data.map(({id, firstName, address, flights}) => (
<p key={id}>
<div>{firstName}</div>
<div>{address.join(', ')}</div>
{flights.map(f => (<div key={f.flightId}>{f.flightId}-{f.seatType}</div>))}
</p>
))}
</div>
);
}
Not sure if it compiles but it's something like this. Also, if you have an ID, use it as key.
I have a object's array of users and i'm using map to show them, each user have a option buttons that is 'edit' and 'remove' options each option have a onlclick function that set a state to show another view so the code explain itselft
class App extends React.Component {
state = {
edit: false,
remove: false
}
handleEdit = () => {
this.setState({ edit: true })
}
handleRemove = () => {
this.setState({ remove: true })
}
cancelEdit = () => {
this.setState({ edit: false })
}
cancelRemove = () => {
this.setState({ remove: false })
}
renderEditItem = () => {
const {
state: {
edit,
remove
},
cancelEdit,
cancelRemove,
handleEdit,
handleRemove
} = this
if (edit) {
return (
<div>
<span>Edit view</span>
<br/>
<button onClick={cancelEdit}>Cancel</button>
</div>
)
}
if (remove) {
return (
<div>
<span>Remove view</span>
<br/>
<button onClick={cancelRemove}>Cancel</button>
</div>
)
}
return (
<div>
<button onClick={handleEdit}>Edit</button>
<br/>
<button onClick={handleRemove}>Remove</button>
</div>
)
}
renderUsers = () => {
const {
renderEditItem
} = this
const users = [
{
id: 1,
name: 'User1'
},
{
id: 2,
name: 'User-2'
},
{
id: 3,
name: 'User-3'
}
]
return users.map((user) => {
return (
<ul key={user.id}>
<li>
<div>
<span ref='span'>{user.name}</span>
<br/>
{renderEditItem()}
</div>
</li>
</ul>
)
})
}
render () {
return (
<div>
{this.renderUsers()}
</div>
)
}
}
React.render(
<App />,
document.getElementById('app')
);
JSfiddle: Here
The issue is how can you see is, when i click on the button to set the state for edit or remove option, this will show the view for all the items,
and should be only the view that is clicked, i know the state change to true and is the same for all the items but i don't know how to set the state only for one entry any idea?
Thank you in advance.
Your problem is that the edit/remove state is singular and for the entire list. Each item in the list receives the same state here:
if (edit) {
return (
<div>
<span>Edit view</span>
<br/>
<button onClick={cancelEdit}>Cancel</button>
</div>
)
}
The single edit variable from the state is applied to each list item. If you want to individually set the edit state for each item, it will need to be kept track of with that item.
EX:
const users = [
{
id: 1,
name: 'User1',
edit: true
}]
This way each individual item will be able to tell what state it is in individually. User1 item will have an edit mode that is independent of the other users.
Then you can render something like this:
return users.map((user) => {
return (
<ul key={user.id}>
<li>
<div>
<span ref='span'>{user.name}</span>
<br/>
{user.edit ? 'EDIT MODE' : 'NOT EDIT MODE'}
</div>
</li>
</ul>
)
})
While looping over an object using map() React can't find its own classes property!
Here is the class of the component,
import React, { Component } from 'react';
import './videolists.css';
export default class VideoLists extends Component {
constructor() {
super();
}
getDefaultLists() {
return [
{
title: 'Iridescent (Official Video) - Linkin Park',
url: 'https://www.youtube.com/watch?v=xLYiIBCN9ec',
id: 'xLYiIBCN9ec'
},
{
title: 'Ed Sheeran - I\'m A Mess (x Acoustic Sessions)',
url: 'https://www.youtube.com/watch?v=-t2CR9qZRj0',
id: '-t2CR9qZRj0'
},
{
title: 'Ed Sheeran - Lego House [Official Video]',
url: 'https://www.youtube.com/watch?v=c4BLVznuWnU',
id: 'c4BLVznuWnU'
}
]
}
itemSelected(itemObject) {
console.log(itemObject);
}
render() {
return (
<div>
<div className='panel panel-default'>
<div className='panel-heading'>
<ul className='list-group'>
{this.getDefaultLists().map(function(item, index){
return <li
key = { index }
className='list-group-item'
onClick={ this.itemSelected.bind(this) }>
{ item.title } <br/>
<small className='listurl'>{ item.url }</small>
</li>;
})}
</ul>
</div>
</div>
</div>
);
}
}
When a user would click on an item it should call the function called itemSelected and also binding the current this element with this.
But when the application is throughing and error.
Here is the error message:
Uncaught TypeError: Cannot read property 'itemSelected' of undefined(…)
How I can call this function in this case from the loop?
you are losing the this context because of your map function. not only do you need to bind that, to get the data object sent though you need to actually tell it to do that. like this.
<ul className='list-group'>
{this.getDefaultLists().map( (item, index) => {
return (
<li key ={index} className='list-group-item' onClick={() => this.itemSelected(item)}>
{ item.title }
<br/>
<small className='listurl'>{ item.url }</small>
</li>
);
})}
</ul>
you can try shadowing your this context, shouldn't be necessary, but worth a shot.
const self = this;
...
<ul className='list-group'>
{self.getDefaultLists().map( (item, index) => {
return (
<li key ={index} className='list-group-item' onClick={() => self.itemSelected(item)}>
{ item.title }
<br/>
<small className='listurl'>{ item.url }</small>
</li>
);
})}
</ul>