I want to render on the page, simple data from manual API, but it seems that I cannot get access to the nested query.
This is what I have:
import React from 'react';
import {
gql,
graphql,
} from 'react-apollo';
//access to queries and map function
const ChannelsList = ({ data: {loading, error, items }}) => {
if (loading) {
return <p>Loading ...</p>;
}
if (error) {
return <p>{error.message}</p>;
}
return (
<div className="channelsList">
{ items.map( itm => <div key={itm.id} className="channel">{itm.sku}</div> ) }
</div>
);
};
const channelsListQuery = gql`
query ChannelsListQuery {
allLinks {
items {
id
sku
}
}
}
`;
export default graphql(channelsListQuery)(ChannelsList);
Don't mind this "channel" names, this is from example that I used for my own api. The problem is, all the queries that contain "id, names, sku" etc is in "items" type and it looks like for me it cannot get access to it. The query in graphqli server is working perfectly. So, the question is- how to get access to nested queries, so that I can use map and render it on page?
Schema structure:
const typeDefs = `
type Link {
items: [itemapi]
}
type itemapi {
id: ID!
sku: String!
name: String!
}
type Query {
allLinks: Link!
}
`;
#Solution by #OzySky
const ChannelsList = ({ data: {loading, error, allLinks }}) => {
if (loading) {
return <p>Loading ...</p>;
}
if (error) {
return <p>{error.message}</p>;
}
return (
<div className="channelsList">
{ allLinks.items.map( aLitm => <div key={aLitm.id} className="channel">{aLitm.sku}</div> ) }
</div>
);
};
In react-apollo, query data is passed through a prop with the name of the query. So in your case you would access items through the data.allLinks prop.
If you have a highly nested return value from your query one way to access the nested object is to first turn it into an array with Object.values(). Then access the field the array items and their corresponding values with dot notation.
const myQuery = gql`
query {
users {
id
name
pet {
id
name
}
}
}
`;
// Inside Render function if using React ...
return users.map(({ id, name, pet }) => (
<div key={id}>
<ul>
<li>
{id} {name}
{Object.values({ pet })[0].name}
</li>
</ul>
</div>
));
Related
I have an array of objects each with name, height and mass. I have a component that gets the names and displays them to the browser. What I'm trying to do is, in a separate component, get the height and mass that correlates to each name.
For example I have:
[
{name: 'Luke Skywalker', height: '172', mass: '77'},
{name: 'C-3PO', height: '167', mass: '75'}
]
I should mention I'm using react for this project. My Component is below:
export default function Character(props) {
const [charStats, setCharStats] = useState("");
const [currentName, setCurrentName] = useState("");
const { name } = props;
useEffect(() => {
axios.get(`${BASE_URL}`)
.then(res => {
setCharStats(res.data);
setCurrentName(name);
})
.catch(err => console.error(err))
}, [])
return (
<div>
<div>{ name }</div>
<button>Stats</button>
{ name === currentName ? charStats.map(char => {
return <Stats height={char.height} mass={char.mass} key={char.name} />;
}) : <h3>Loading...</h3>
}
</div>
)
}
The name prop I am getting from another component, I can console.log it and get each individual name so I know that works. But with the code above, I am getting the height and mass of every object returned instead of just the ones that match the name. How can I get specifically the height and mass of each object?
Looks like you might want to call filter before using map, like for example: data.filter(x => x.name === name).map(char => {.... which returns a collection that only contains the elements that match the condition). Or if you only want to find one element, its better to use .find(x => x.name === name) instead
I'm trying to make my first real React app and am pulling information from a database, updating the state to set that info as an array, and then trying to access the properties of the objects in the array.
function App() {
const [students, setStudents] = useState([]);
function fetchStudentInfo() {
fetch('https://api.hatchways.io/assessment/students')
.then(response => {
return response.json();
})
.then(data => {
const transformedStudentData = data.students.map(studentData => {
return {
imgURL: studentData.pic,
fullName: `${studentData.firstName} ${studentData.lastName}`,
email: studentData.email,
company: studentData.company,
skill: studentData.skill
}
});
setStudents(transformedStudentData);
});
}
fetchStudentInfo();
return (
<div> {console.log(students[0].fullName)}
<h1>Student Assessments</h1>
</div>
)
}
export default App;
I know I shouldn't be console.logging the info I'm trying to get, but I'm not even sure how to use the console in React to find out how to access my variables. Anyway, I get "TypeError: Cannot read properties of undefined (reading 'fullName')" as an error and it fails.
I'm really trying to pass down the array as properties to be used in my components, but I've taken out code to try and simplify the problem for myself and this is where I've hit a wall.
On the first render your students state is an empty array because that's how you've initialised it. On that first render you can't access the first index (because the data hasn't been fetched, or the state updated), and you can't access a property of an element that doesn't exist yet.
So you need a condition in there that renders: 1) if students.length is zero return a loading message (for example) 2) otherwise map over the students array which you now know exists to produce a list of student names.
Here's a working example that uses useEffect to add an array to state after three seconds simulating your API fetch. You should be using useEffect like this for your fetch (with an empty dependency array) instead of calling your function directly in the component.
const { useEffect, useState } = React;
const json = '[{ "fullName": "Bob" },{ "fullName": "Karen" },{ "fullName": "Rick" }]';
function mockApi() {
return new Promise((res, rej) => {
setTimeout(() => res(json), 3000);
});
}
function Example({ data }) {
const [ students, setStudents ] = useState([]);
useEffect(() => {
function getData() {
mockApi()
.then(json => JSON.parse(json))
.then(data => setStudents(data));
}
getData();
}, []);
// Initial log will be `[]`
// Second log will be the updated state stringified
useEffect(() => {
console.log(JSON.stringify(students));
}, [students]);
// No data, so return the loading message, or spinner
if (!students.length) return <div>Loading: wait 3 seconds</div>
// Data! `map` over it to produce the list
return (
<ul>
{students.map(student => {
return <li>{student.fullName}</li>;
})}
</ul>
);
};
ReactDOM.render(
<Example />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I have the following component named Tweets.js:
import React, {Component} from "react";
export default class Tweets extends Component {
constructor(props) {
super(props);
this.state = {tweets: [], users: []};
}
componentDidMount() {
this.TweetList();
this.UserList();
}
TweetList() {
fetch('http://***.***.***.***:****/api/posts')
.then(res => res.json())
.then((data) => {
this.setState({ tweets: data })
})
.catch(console.log);
}
UserList() {
fetch('http://***.***.***.***:****/api/users')
.then(res => res.json())
.then((data) => {
this.setState({ users: data })
})
.catch(console.log);
}
render() {
const tweets = this.state.tweets.map((item, i) => (
<div>
<div class="tweet_box">
<p>{item.tweet}</p>
</div>
</div>
));
const users = this.state.users.map((item, i) => (
<div>
<h2>{item.twitterhandle}</h2>
</div>
))
return (
<div className="tweet">{ tweets }</div>
)
}
}
users json:
[
{
"userid": 337282,
"twitterhandle": "sean",
"email": "sean#sean.com"
},
{
"userid": 1234563,
"twitterhandle": "DidItWork",
"email": "pleasework#work.com"
},
{
"userid": 3728,
"twitterhandle": "Isthisup",
"email": "isthisup#up.com"
},
{
"userid": 1234567,
"twitterhandle": "blah",
"email": "matt#knop.com"
}
]
posts json:
[
{
"postid": 2,
"tweet": "Hello, World #2",
"userid_id": 337282
},
{
"postid": 4,
"tweet": "Hello, World #1",
"userid_id": 3728
},
{
"postid": 1,
"tweet": "Hello, World #4",
"userid_id": 3728
},
{
"postid": 3,
"tweet": " Hello, World! How's it?",
"userid_id": 1234563
}
]
I am trying to return the list of Posts from the posts json file and I was able to accomplish that. My question is, how am I able to take the userid_id number, find it in the users json file, pull the username, and display it inside the following code?
const tweets = this.state.tweets.map((item, i) =>
<div>
<div class="tweet_box">
<p>{item.tweet}</p>
</div>
</div>
));
All I have been able to accomplish is to get it to display a separate list of the users but I can't figure out how to route the data into the above code - in say, an h1 tag.
I am very new to react and I apologize if my code is terrible. I am open to any and all suggestions or reference I can look at. I looked through the React documentation and found the section on keys and it appeared to be the correct direction, but still couldn't figure it out.
If there is anything I can add to clarify my issue, please don't hesitate to ask.
I appreciate all of your time.
EDIT: I am attempting to work through the problem some more and added the following function:
GetUsername() {
return this.state.users[1];
}
I then made the following changes to my render:
const tweets = this.state.tweets.map((item, i) => (
<div>
<div class="tweet_box">
<h1>{this.GetUsername()}</h1>
<p>{item.tweet}</p>
</div>
</div>
));
I added
<h1>{this.GetUsername()}</h1>
Which produced the following error:
Error: Objects are not valid as a React child (found: object with keys
{userid, twitterhandle, email, password}). If you meant to render a
collection of children, use an array instead.
You are close. React doesn't render objects directly but can render primitives, like strings and numbers. this.state.users[1] will always return the second user object in the users array, which probably isn't what you want.
Update your utility to take an id and find the correct user object.
getUserById = (id) => this.state.users.find(({ userid }) => userid === id);
Then call that in the render to get the user object and if found access the twitterhandle property.
const tweets = this.state.tweets.map((item, i) => (
<div>
<div class="tweet_box">
<h1>{this.getUserById(item.userid_id)?.twitterhandle}</h1>
<p>{item.tweet}</p>
</div>
</div>
));
This GetUserName method returns an object (the user's object). It cannot render an object, but it can render a string or a number. for example:
GetUsername() {
return this.state.users[1].twitterhandle;
}
Also you can return a array and iterate with a map like you do in the const "tweet"
The error you are getting, "Objects are not valid as a React child", is because your GetUsername function actually returns the whole user object instead of just the username. You need to change this.state.users[1] to this.state.users[1].twitterhandle.
But we want to find the right user for each tweet, not just return the first user all the time. We do this by looking through the users array and finding the element where user.userid matches tweet.userid_id.
Let's make a getUsername function that takes the numeric user id as an argument:
getUsername( id ) {
const user = this.state.users.find( user => user.id === id );
if ( user ) {
return user.twitterhandle;
} else {
return ''; // fallback to return an empty string if no user was found
}
}
(You can write that a lot more concisely, but I wanted to be clear about the intention)
Now we can call that with the id from the tweet item
const tweets = this.state.tweets.map((item, i) => (
<div>
<div class="tweet_box">
<h1>{this.getUsername(item.userid_id)}</h1>
<p>{item.tweet}</p>
</div>
</div>
));
So I am doing an SQL query in my node/express backend and sending the result to my React front end through JSON. I am console.log to see what info I am sending and that seems to be in order. However, I can't seem to figure out how to set this array of objects to my state. I am getting this error:
"Objects are not valid as a React child (found: object with keys {Title}). If you meant to render a collection of children, use an array instead."
Here is the component I am working with:
import React, { Component } from 'react'
export class ShoppingCart extends Component {
state = {
cartItems: ['Title 1']
};
displayCart = () => {
console.log('call it')
console.log('hello from displaycart');
fetch('http://localhost:5000/fillCart')
.then((res) => res.json())
.then((json) => {
this.setState(state => {
const cartItems = state.cartItems.concat(json.Title);
return {
cartItems
};
});
})
}
componentDidMount() {
this.displayCart();
}
render() {
return (
<div className="ShoppingCart">
<h3>This is your shopping cart</h3>
<ul>
{this.state.cartItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
</div>
)
}
}
export default ShoppingCart
My goal is to set the json.Title (right now holding multiple titles) into the cartItems State array I have in my state, then display each title through render() in a list. However, I am stuck as to how to accomplish this.
Also, here is what the json data looks like that I send from the Node/express backend:
[
RowDataPacket { Title: 'The Tragedy of The Korosko' },
RowDataPacket { Title: 'Two in the Far North' }
]
To clarify, at this point my problem is not displaying the information, but rather setting it to the state in the first place.
Any suggestions sure would be appreciated! I've been stuck on this for several days. Thanks in advance!!!
You need to extract all titles from the json response.
This is what the response format looks like:
{
Title: [{Title: ''}, {Title: ''}]
}
Your top level json.Title is an array with objects.
So the .Title property does not exist on json.Title, but on each of the objects inside of the array.
We can use map to pull out what we need.
Your setState could look something like this:
this.setState(state => {
// All titles in an array
const titles = json.Title.map(row => row.Title)
// Add to existing ones
const cartItems = state.cartItems.concat(titles);
return {
cartItems
};
});
Oskar Hane had an answer that was pretty close. Here is his answer with a slight modification:
.then((json) => {
this.setState(state => {
const titles = json.Title.map(row => row.Title)
const cartItems = state.cartItems.concat(titles);
return {
cartItems
};
});
})
}
The change is in json.Title.map. It is necessary to map through an array; however, json.map is not mapping through an array because json is not an array. json.Title is the array.
Use Array Spreading with current state and the JSON in response. Code will be like bellow:
displayCart = () => {
fetch('http://localhost:5000/fillCart')
.then((res) => res.json())
.then(json => {
const newItemsArry = json.map(itm=> itm.Titles)
this.setState({cartItems: [...this.state.cartItems, newItemsArry]}))
}
}
I have an array of Objects like
const array = [{
text: 'some text',
userID: '1'
},
{
text: 'another text',
userID: '2'
}
]
I have to map over this array in React and also get the user details like username & user-dp through the userID. How should I do it. I am using React, MongoDB, NodeJS, Express.
I have tried calling the api to get user-details while mapping the array in my frontend code. It does not render properly as I learned later that we should not call api in the render() method.
Well your question is not complete, but I will give it a try...
Assuming you have:
const array = [{
text: 'some text',
userID: '1'
},
{
text: 'another text',
userID: '2'
}
];
You can do
async componentDidMount() {
// I dont know where the array come from, ill assume it comes from the props...
const { array } = this.props;
// Promise.all returning an array of userDetails in the same order than the given array
const userDetails = await Promise.all(array.map(({userID}) => fetch(`apiUrl/${userID}`);
this.setState({ userDetails })
};
And then you will have access to your details in the render by accessing to your state.
I see this thread is old but i'd like to share my answer anyway.
Since your working with react i'd make a component for every render. Give the component the userId as a prop and use useEffect and useState within the component to fetch the user with the given id and store it in the components state.
So in your rendering view it'll look like:
return (
{
array.map(item => {
return <CustomComponent item={item} />
})
}
And your component will look like:
const CustomComponent = ({ item }) => {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(item.userID, setUser);
}, [])
return (
<div>
{user && <p>{user.something}</p>}
<p>{item.text}</>
</div>
);
}