Access query inside return method - javascript

I have a backend Drupal site and react-native app as my frontend. I am doing a graphQL query from the app and was able to display the content/s in console.log. However, my goal is to use a call that query inside render return method and display it in the app but no luck. Notice, I have another REST API call testName and is displaying in the app already. My main concern is how to display the graphQL query in the app.
Below is my actual implementation but removed some lines.
...
import gql from 'graphql-tag';
import ApolloClient from 'apollo-boost';
const client = new ApolloClient({
uri: 'http://192.168.254.105:8080/graphql'
});
client.query({
query: gql`
query {
paragraphQuery {
count
entities {
entityId
...on ParagraphTradingPlatform {
fieldName
fieldAddress
}
}
}
}
`,
})
.then(data => {
console.log('dataQuery', data.data.paragraphQuery.entities) // Successfully display query contents in web console log
})
.catch(error => console.error(error));
const testRow = ({
testName = '', dataQuery // dataQuery im trying to display in the app
}) => (
<View>
<View>
<Text>{testName}</Text> // This is another REST api call.
</View>
<View>
<Text>{dataQuery}</Text>
</View>
</View>
)
testRow.propTypes = {
testName: PropTypes.string
}
class _TestSubscription extends Component {
...
render () {
return (
<View>
<FlatList
data={this.props.testList}
...
renderItem={
({ item }) => (
<testRow
testName={item.field_testNameX[0].value}
dataQuery={this.props.data.data.paragraphQuery.entities.map((dataQuery) => <key={dataQuery.entityId}>{dataQuery})} // Here I want to call the query contents but not sure how to do it
/>
)}
/>
</View>
)
}
}
const mapStateToProps = (state, ownProps) => {
return ({
testList: state.test && state.test.items,
PreferredTest: state.test && state.test.PreferredTest
})
}
...

There are few different things that are wrong there.
Syntax error is because your <key> tag is not properly closed here:
(dataQuery) => <key={dataQuery.entityId}>{dataQuery})
And... there is no <key> element for React Native. You can check at docs Components section what components are supported. Btw there is no such an element for React also.
Requesting data is async. So when you send request in render() this method finishes execution much earlier before data is returned. You just cannot do that way. What can you do instead? You should request data(in this element or its parent or Redux reducer - it does not matter) and after getting results you need to set state with .setState(if it happens inside the component) or .dispatch(if you are using Redux). This will call render() and component will be updated with data retrieved. There is additional question about displaying spinner or using other approach to let user know data is still loading. But it's orthogonal question. Just to let you know.
Even if requesting data was sync somehow(for example reading data from LocalStorage) you must not ever do this in render().This method is called much more frequently that you can expect so making anything heavy here will lead to significant performance degradation.
So having #3 and #4 in mind you should run data loading/fetching in componentDidMount(), componentDidUpdate() or as a part of handling say button click.

Related

Cannot map data correctlly

I'm new to React.js development and need help with this issue. I created a service using FastAPI, which executes a number of SQL SELECT declarations against an Oracle database, takes the results, and exposes them as a REST API using JSON data format.
In a Chrome Web-browser, I see this information when I execute a GET to the API:
[API GET results][1]
Therefore, the API is returning results correctly. To show them all in my React app, I created this component:
import React, {Component} from 'react';
export default class App extends React.Component {
state = {
loading: true,
pedidos: []
}
async componentDidMount() {
const url = "http://localhost:8000/pedidos-bloqueados/";
fetch(url)
.then(response => {
return response.json();
})
.then(d => {
this.setState({ pedidos: [d], loading: false });
})
.catch(error => console.log(error))
}
render() {
return (
<div>
{ this.state.loading? <div>Carregando...</div> : <div><li> Cliente: * {(this.state.pedidos.map((pedido, index)=>(this.state.pedidos[index]))).map((p, i)=> p['cliente'])}</li></div> } *
</div>
);
}
}
It turns out that, when a map() function inside render() method gets executed, the only information shown is:
Cliente:
Without any more data. And the component is not allowed to do a for() loop inside render() method, what could, possibly, render the objects encoded in the JSON list.
I think the problem is in the snippet:
{ this.state.loading? <div>Loading...</div> : <div><li> Cliente: {(this.state.pedidos.map((pedido, index)=>(this.state.pedidos[index]))).map((p, i)=> p['cliente'])}</li></div> }
How should I change my code to show the properties of each object in the list when rendering the component? (Note: cliente is ***just one of the JSON object attributes - there are others like vendedor, pedido, and so on... But, if I manage to list this field, I can replicate the behavior to the others).
I thank you VERY MUCH for any assistance with this matter!
[1]: https://i.stack.imgur.com/XHuN3.png

Integrating Firebase Realtime Database with React Native App

I am for the first time learning about state, and followed a simple tutorial to create a react native app. The tutorial did not cover using firebase, so this is what I've pieced together. It "works", but does not pull the database data on the first render. I know it's because of the delay in time it takes to grab the data vs my app rendering. But I don't know how I should move logic around to fix it. I feel like I should be using the .then somehow? Or am I doing all of this completely wrong...
import {db} from '../../src/config.js';
let initialMessages = [];
db.ref().once('value', (snapshot) =>{
snapshot.forEach((child)=>{
child.forEach(function(childChildSnapshot) {
initialMessages.push({
id: childChildSnapshot.key,
title: childChildSnapshot.val().title,
})
})
})
})
.then()
.catch((error) => {console.error('Error:', error)});
function MessagesScreen(props) {
const [messages, setMessages] = useState(initialMessages);
return (
<Screens>
<View style={styles.wholeThing}>
<FlatList
data={messages}
keyExtractor={(messages) => messages.id.toString()}
renderItem={({ item }) => (
<Card
title={item.title}
onPress={() => console.log("hi")}
/>
)}
ItemSeparatorComponent={ListItemSeparator}
contentContainerStyle={styles.messagesList}
refreshing={refreshing}
onRefresh={}
/>
</View>
</Screens>
);
}
export default MessagesScreen;
By the time you pass initialMessages to the state hook (as its initial value), the initialMessages.push(...) hasn't been called yet.
Instead, you need to call setMessages when the data has loaded:
db.ref().once('value', (snapshot) =>{
snapshot.forEach((child)=>{
child.forEach(function(childChildSnapshot) {
initialMessages.push({
id: childChildSnapshot.key,
title: childChildSnapshot.val().title,
})
})
setMessages(initialMessages);
})
})
Calling setMessages will then rerender the (affected) UI, and that will show the messages.
This of course means that you'll need to pull the useState hook out of MessagesScreen, so that it's also visible in the location where you now call setMessages.

Is it possible to fetch/parse JSON data from one component to a different rendered/dynamic route? (React JS)

I am new to reactJS and just started learning it for a coding assignment, but I am stuck on this one problem. I created an api in a component that FETCHes photo and photo album data from the online JSON prototyping website, and I want to use that data to display on a user page (only photos and albums related to that specific user/userID). The user page is a dynamic child component of the parent component (the home page). here is what it looks like so far
This is for the JSON data. I call it apiData
import React, { Component } from "react";
class apiData extends Component {
state = {
photos: [],
albums: [],
isLoading: false
};
componentDidMount() {
this.loadData();
}
loadData = async () => {
const photos = await fetch(
"https://jsonplaceholder.typicode.com/photos"
);
const albums = await fetch(
"https://jsonplaceholder.typicode.com/albums"
);
const photoData = await photos.json();
const albumData = await albums.json();
this.setState({ isLoading: true, photos: photoData, albums: albumData});
};
render() {
return (
<div className="App">
<p> dataObject={this.state.results}</p>
</div>
);
}
}
This is the rendered/dynamic route. currently, I am using
const userPhoto = apiData.photos.find((userPhoto) =>{
to try to access the API, but I get an error that says
TypeError: Cannot read property 'find' of undefined"
(keep in mind, these 2 components are coded on the same .js file)
const userPhotoPage = ({match}) => {
const userPhoto = apiData.photos.find((userPhoto) =>{
return parseInt(match.params.id) == userPhoto.id
})
return <>
{match.isExact && <>
<h1>{userPhoto.title}</h1>
<p>image = {userPhoto.url}</p>
</>}
</>
}
I want my userPhotoPage function to successfully access the data from apiData, so I can manipulate it and add data to the photo page, but I'm not sure if this is possible? If I need to provide more information I'll gladly give it!
It looks like your trying to access the find method on an undefined object. First off, the .find() method is an iterator on arrays. I tried fetching the data from your JSON photos API but for some reason, it's hanging on this end. I'm not sure if that API actually returns an array. I would look into that first. Next, even if it returns an array and your able to chain the find method on it you're still left with the issue of function scope. The photo objects is scoped to/private your loadData async funtion. You will not be able to access it outside of the function.

How can manipulate redux data after dispatch it?

I have a search screen, contain Input And TopTabs "Songs, Artists",
When I get data from API after a search I make two things
1- I setState to appear the TopTab Component "true/false"
2- dispatch an action to save Songs & Artists Data in redux store.
that works fine.
But in topTab component, as I say before I have tow tabs "songs, artists"
For example, In the Songs component, I want to manipulate the data to achieve my case so in componentDidMount I Map the songs array from redux and push the new data into the component state.
But it's not working fine!
At the first time, I got songs from redux as empty [] although it's saved successfully in redux store when I get data from API
So how can I handle this case to not mutate the data?
Search.js "Main screen"
onSearch = async () => {
const {searchText} = this.state;
if (searchText.length > 0) {
this.setState({onBoarding: false}); // to appear the TopTab Component
try {
let response = await API.post('/search', {
name: searchText,
});
let {
data: {data},
} = response;
let artists = data.artists.data;
let songs = data.traks.data;
this.props.getResult(songs, artists);
}
catch (err) {
console.log(err);
}
}
render(){
<View style={styles.searchHeader}>
<Input
onChangeText={text => this.search(text)}
value={this.state.searchText}
onSubmitEditing={this.onSearch}
returnKeyType="search"
/>
</View>
{this.state.onBoarding ? (
<SearchBoard />
) : (
<SearchTabNavigator /> // TopTabs component
)}
}
SongsTab
...
componentDidMount() {
console.log('props.songs', this.props.songs); // Empty []
let All_tunes = [];
if (this.props.songs?.length > 0) {
console.log('mapping...');
this.props.songs.map(track =>
All_tunes.push({
id: track.id,
name: track.name,
url: URL + track.sounds,
img: URL + track.avatar,
}),
);
this.setState({All_tunes});
}
}
...
const mapStateToProps = state => {
return {
songs: state.searchResult.songs,
};
};
Edit
I fix the issue by using componentDidUpdate() life cycle
If you have any other ways tell me, please!
SongsTab
manipulateSongs = arr => {
let All_tunes = [];
arr.map(track =>
All_tunes.push({
id: track.id,
name: track.name,
url: URL + track.sounds,
img: URL + track.avatar,
}),
);
this.setState({All_tunes});
};
componentDidMount() {
if (this.props.songs?.length > 0) {
this.manipulateSongs(this.props.songs);
console.log('mapping...');
}
}
componentDidUpdate(prevProps) {
if (prevProps.songs !== this.props.songs) {
this.manipulateSongs(this.props.songs);
}
}
The problem you're referring to has to do with the way asynchronous code is handled in JavaScript (and in turn react-redux). When your component initially mounts, your redux store passes its initial state to your SongsTab.js component. That seems to be an empty array.
Any API call is an asynchronous action, and won't update the redux store until the promise has resolved/rejected and data has been successfully fetched. Any HTTP request takes much longer to complete than painting elements to the DOM. So your component loads with default data before being updated with the response from your API call a number of milliseconds later.
The way you've handled it with class-based components is fine. There are probably some optimizations you could add, but it should work as expected. You might even choose to render a Spinner component while you're fetching data from the API as well.
If you want a different approach using more modern React patterns, you can try and use the equivalent version with React hooks.
const Songs = ({ fetchSongs, songs, ...props }) => {
React.useEffect(() => {
// dispatch any redux actions upon mounting
// handle any component did update logic here as well
}, [songs])
// ...the rest of your component
}
Here are the docs for the useEffect hook.

Implement RSS feed in React native

So, I've been learning react native lately and I am trying to create an RSS reader.I have managed to Download the RSS data but I am stuck in implementing them into JSX ( Screen). I've been trying using setState method but it didn't work.
I can get the RSS data logged into Console but I can't display them in user through JSX
PS: Comments are just tests that I made
class HomeScreen extends React.Component {
state = {
feed: {},
items: {}
};
RSS() {
return fetch("http://di.ionio.gr/feed/")
.then(response => response.text())
.then(responseData => rssParser.parse(responseData))
.then(rss => {
this.setState(() => ({
//...prevState,
feed: rss
//titles: rss.items,
}));
});
}
render() {
{this.RSS();}
return (
<View style={styles.top}>
<HeaderScreen {...this.props} />
<Text>{this.state.feed.title}</Text>
</View>
);
}
}
I've been using react-native-rss-parser.
I also tried without setState but that also didn't work.
Thanks in advance
With your current setup, you're creating an endless loop. This is because you have a side effect (i.e. network request) in your render function.
When your request returns and you call setState your component is rendered again, which in turn calls the network again with this.RSS(), etc. etc. etc.
To fix this, move the this.RSS() to either your constructor function or better yet to the componentDidMount function.
i.e.
componentDidMount() {
this.RSS();
}
render() {
return (
<View style={styles.top}>
<Text>{this.state.feed.title}</Text>
</View>
);
}

Categories

Resources