Can't access the data of Facebook Graph Api response - reactJs - javascript

I tried all the day to find a solution on how I can access Facebook nested response.
I use FB.api with a nested request and I got the response and it is ok.
Here a sample of the Response I got from Facebook Graph Api
{
"insights": {
"data": [
{
"name": "page_fan_adds_unique",
"period": "month",
"values": [
{
"value": 272,
"end_time": "2019-10-08T07:00:00+0000"
},
{
"value": 270,
"end_time": "2019-10-09T07:00:00+0000"
}
],
"title": null,
"description": "The number of new people who have liked your Page.",
"id": "1021596371302281/insights/page_fan_adds_unique/month"
},
{
"name": "page_fan_removes_unique",
"period": "month",
"values": [
{
"value": 450,
"end_time": "2019-10-08T07:00:00+0000"
},
{
"value": 453,
"end_time": "2019-10-09T07:00:00+0000"
}
],
"title": null,
"description": "Unlikes of your Page.",
"id": "1021596371302281/insights/page_fan_removes_unique/month"
},
{
"name": "page_views_total",
"period": "month",
"values": [
{
"value": 6430,
"end_time": "2019-10-08T07:00:00+0000"
},
{
"value": 6339,
"end_time": "2019-10-09T07:00:00+0000"
}
],
"title": null,
"description": "The number of times a Page's profile has been viewed by logged in and logged out people.",
"id": "1021596371302281/insights/page_views_total/month"
}
],
i'm using react. I made the APi call in the parent component like so :
async componentDidMount() {
const resI = await axios.get(
'https://graph.facebook.com/MyID/',
{
params: {
access_token: process.env.REACT_APP_FACEBOOK_ACCESS_TOKEN,
fields:
'insights.metric(page_fans, post_engaged_users, post_impressions_unique, page_fan_adds_unique, page_fan_removes_unique, page_views_total, page_fans_gender_age, page_fans_country, page_fans_city, page_fans_locale).period(month)'
}
}
);
this.setState({insights: resI.data});
console.log(this.state.insights.insights.data[0].values[1].value); //I Get 270. It working in the parent component
}
I past the data I got from the API as a props to my other components
....
<div>
<GlobalPerf insights={this.state.insights} />
</div>
......
Here is where the problem start. I can't access value in the values array within my child that components
class GlobalPerf extends Component {
render() {
const ins = this.props.insights; // I can't go deeper than that.
console.log(ins.insights);
I can't access the value in the object. I can't go deeper than this.props.insights
When I try this.props.insights.data it not working. Can someone help me figuring out ? Thx

this.setState({insights: resI.data});
console.log(this.state.insights.insights.data[0].values[1].value); //I Get 270. It working in the parent component
The above statements should not work, since the setState is asynchronous and does not update the value synchronously. Probably showing the old state value?
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
You may be trying to access the props.insights before axios completes
const ins = this.props.insights; // probably wait for the axios to complete the fetch before trying to access the value! write it as
console.log(ins && ins.insights); //will give insights once the component rerenders after the props change due to the state being updated in the parent after axios returns!

Related

How to navigate to the last child of a hierarchical data stored in SQL database based on the JSON passed in the request body in nodeJS?

I have a need to get the last child of a hierarchy based on the JSON data passed from the request body.
The request body looks like this:
{
"hierarchy": [
{
"id": 1,
"name": "A",
"level": 0,
"labels": [
"grandparent",
"parent",
"child"
],
"categories": [
{
"id": 2,
"name": "B",
"level": 1,
"action": "kill",
"categories": [
{
"id": 3,
"name": "C",
"level": 2,
"action": "kill",
"categories": []
}
]
}
],
"action": "kill"
}
]
}
The data will be stored in the SQL database like this:
Based on the data sent from the req body, I need to navigate to the last child in the database , in this case: C and then delete them.
Currently Im using this:
const killfamily = async (id, transaction, header = {}) => {
//need recursive method here
await family.destroy({ where: { parent_id: id }, transaction: transaction });
const result = await family.destroy({ where: { fid: id }, transaction: transaction });
return result;
Current approach is not working as im trying to delete the parent first. I want to achieve this using recursive method i.e Go to the inner most/ last child and perform destroy on it.
Any help is appreciated. Thank you

how to hit a specific end point using json server

I have set up json-server using a react app and I had a quick question about end points
this is some of my data:
{
"products": [
{
"name": "football",
"id": "SPO-001",
"category": "sport",
"price": "40",
"coupons": ["daw124qdw", "a1212cxn"]
},
{
"name": "cricket bat",
"id": "SPO-002",
"category": "sport",
"price": "80"
}
]
}
and it all works fine when i do something like this:
axios.get('http://localhost:3004/products').then(({ data }) => {
// do something with data
})
and I know I can do: http://localhost:3004/products/SPO-001 which returns me the id for that. but how can I just return the coupons entry for that specific id? is that a possible endpoint?
I tried axios.get('http://localhost:3004/products/SPO-001/coupons') but I just got an empty object. any ideas?
You have 2 options:
1st
axios.get('http://localhost:3004/products')
.then(res => this.setState({product: res.data.productID.coupon})) // not sure how you want to use your response, this is just an example
2nd create your endpoint for each product, something like http://localhost:3004/:productID/coupon and then "hit" that endpoint with your axios get request, e.g. axios.get('http://localhost:3004/23/coupon').then(res => res.data)
I would have to say the 2nd option would be more robust.

How can I get all results in ReactiveList

I've tried to use on all data but it doesn't work since I can get only the number of results as stated in the 'size' parameter. Here part of my code.
<ReactiveList
componentId="results"
dataField="original_title"
size={1}
showResultStats={true}
pagination={true}
react={{
and: "searchbox"
}}
onAllData={this.onAllData}
/>
onAllData(shops) {
let result = null;
if (shops !== null) {
console.log(shops.length);
result = shops.map((marker) => (
<ListItem>
<Thumbnail square size={80} source={{ uri: 'https://dummyimage.com/80x80/000/fff' }} />
<Body>
<Text>{marker.name}</Text>
<Text note>{marker.dis}</Text>
</Body>
</ListItem>
))
return result;
}
}
There are a couple of ways you can go about this problem:
Rendering all results with infinite loading
onAllData will give the number of results specified in the size prop. Usually, its not a good idea to set a very high size since it will take more time fetching and rendering the results. A good alternative for this is to use infinite scrolling by setting the pagination prop to false and setting a value in size prop which tells the component how many results to fetch when you reach the end of the list.
Fetching all results using scroll API
tl;dr Demo
Note
This answer uses reactivesearch for web (for demonstration) but you can use the same props in reactivesearch-native since the API is same.
The above approach works fine if you're only interested in rendering the results. But if you wish to fetch all the results for the current query, you can use the scroll API from Elasticsearch. You can use ReactiveList to get the current query and then use it together with the scroll API.
For this purpose, you can make use of the onQueryChange prop on ReactiveList:
First specify an onQueryChange prop on the ReactiveList which receives the previous and current query as parameters:
onQueryChange={(prev, next) => ...}
This function will be invoked each time the query is changed so you can write a logic to fetch the hits for the current query (received in the next parameter) whenever required.
A scroll API call returns the results in the following format:
{
"_scroll_id": "DnF1ZXJ5VGhlbkZldGNoAgAAAAClGlY4FlotbmJJZXA0U09lMlZFMUNyQ3M2M0EAAAAApRpWORZaLW5iSWVwNFNPZTJWRTFDckNzNjNB",
"took": 0,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 9407,
"max_score": 1,
"hits": [
{
"_index": "good-books-ds",
"_type": "good-books-ds",
"_id": "5676",
"_score": 1,
"_source": {
"authors": "Arthur C. Clarke, Gentry Lee",
"average_rating": 3.76,
"average_rating_rounded": 4,
"books_count": 48,
"id": 5676,
"image": "https://images.gr-assets.com/books/1375814957l/112518.jpg",
"image_medium": "https://images.gr-assets.com/books/1375814957m/112518.jpg",
"isbn": "1857230213",
"language_code": "eng",
"original_publication_year": 1991,
"original_series": "Rama",
"original_title": "The Garden of Rama (Rama, #3)",
"ratings_count": 16389,
"title": "The Garden of Rama (Rama, #3)"
}
},
{
"_index": "good-books-ds",
"_type": "good-books-ds",
"_id": "5681",
"_score": 1,
"_source": {
"authors": "Darren Shan",
"average_rating": 4.22,
"average_rating_rounded": 4,
"books_count": 52,
"id": 5681,
"image": "https://s.gr-assets.com/assets/nophoto/book/111x148-bcc042a9c91a29c1d680899eff700a03.png",
"image_medium": "https://s.gr-assets.com/assets/nophoto/book/111x148-bcc042a9c91a29c1d680899eff700a03.png",
"isbn": "",
"language_code": "",
"original_publication_year": 2003,
"original_series": "Cirque Du Freak",
"original_title": "Killers of the Dawn (Cirque Du Freak, #9)",
"ratings_count": 18194,
"title": "Killers of the Dawn (Cirque Du Freak, #9)"
}
},
{
"_index": "good-books-ds",
"_type": "good-books-ds",
"_id": "5683",
"_score": 1,
"_source": {
"authors": "Laura Joffe Numeroff, Felicia Bond",
"average_rating": 4.16,
"average_rating_rounded": 4,
"books_count": 13,
"id": 5683,
"image": "https://s.gr-assets.com/assets/nophoto/book/111x148-bcc042a9c91a29c1d680899eff700a03.png",
"image_medium": "https://s.gr-assets.com/assets/nophoto/book/111x148-bcc042a9c91a29c1d680899eff700a03.png",
"isbn": "60278684",
"language_code": "",
"original_publication_year": 2000,
"original_series": "",
"original_title": "If You Take a Mouse to the Movies",
"ratings_count": 17938,
"title": "If You Take a Mouse to the Movies"
}
},
{
"_index": "good-books-ds",
"_type": "good-books-ds",
"_id": "5685",
"_score": 1,
"_source": {
"authors": "Orson Scott Card, James Cameron",
"average_rating": 4.06,
"average_rating_rounded": 4,
"books_count": 15,
"id": 5685,
"image": "https://images.gr-assets.com/books/1225165505l/40289.jpg",
"image_medium": "https://images.gr-assets.com/books/1225165505m/40289.jpg",
"isbn": "99690608",
"language_code": "eng",
"original_publication_year": 1989,
"original_series": "",
"original_title": "The Abyss",
"ratings_count": 16318,
"title": "The Abyss"
}
},
{
"_index": "good-books-ds",
"_type": "good-books-ds",
"_id": "5687",
"_score": 1,
"_source": {
"authors": "Katarina Bivald, Alice Menzies",
"average_rating": 3.56,
"average_rating_rounded": 4,
"books_count": 63,
"id": 5687,
"image": "https://images.gr-assets.com/books/1452107441l/25573977.jpg",
"image_medium": "https://images.gr-assets.com/books/1452107441m/25573977.jpg",
"isbn": "149262344X",
"language_code": "eng",
"original_publication_year": 2013,
"original_series": "",
"original_title": "Läsarna i Broken Wheel rekommenderar",
"ratings_count": 14571,
"title": "The Readers of Broken Wheel Recommend"
}
}
]
}
}
The value received as _scroll_id can be passed to the scroll API to fetch the next set of results and so on till the number of hits is zero.
Note
If your cluster has a lot of data, its not a good idea to run this logic to fetch all the results every time the query changes. You can add a condition to limit the number of results fetched or store the current query in state and only fetch all the results when required.
Here's an example on how you can implement this with ReactiveList. In the example I'm fetching the results each time the query changes but you can modify it to fetch results conditionally instead:
In your render function:
<ReactiveList
...
size={10}
onQueryChange={this.handleQueryChange}
/>
Here's how the handleQueryChange function can look like. This will give you all the results for the current query:
handleQueryChange = async (prev, next) => {
// avoid fetching the results for match_all query since dataset is large
if (next && !next.query.match_all) {
console.log('Fetching all results for query:', next);
// modify the query size here if needed (currently it is 10)
// initial url to obtain scroll id is different
const initialResults = await this.fetchResults(next, url);
// keep scrolling till hits are present
// NOTE: careful if you've a lot of results,
// in that case you might want to add a condition to limit calls to scroll API
const scrollResults = await this.fetchScrollResults({
scroll: "1m",
scroll_id: initialResults._scroll_id
});
// combine the two to get all results
// concat hits from initialResults with hits from scrollResults
const allResults = initialResults.hits.hits.concat(scrollResults);
console.log(`${allResults.length} results found:`, allResults);
}
};
Its using two functions for fetching the results initially and later with the scroll_id. The endpoints for both are different, which you can find in the demo. Here's how the first fetchResults looks like:
fetchResults = (query, api) => {
return fetch(api, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Basic ${btoa(credentials)}`
},
body: JSON.stringify(query)
})
.then(res => res.json())
.catch(err => console.error(err));
};
The fetchScrollResults will use the scroll API to fetch results till the hits obtained are 0.
fetchScrollResults = async query => {
const res = await this.fetchResults(query, scrollUrl);
const { hits } = res.hits;
if (hits.length) {
return [
...hits,
...(await this.fetchScrollResults({
scroll: "1m",
scroll_id: res._scroll_id
}))
];
}
return [];
};
Check the demo, results will appear in the console.
You are missing a key that identify unique components and you also need to wrap element inside function. If function is called properly and your array have elements this should work.
onAllData(shops) {
let result = null;
if (shops !== null) {
console.log(shops.length);
result = shops.map((marker,index) => { return (
<ListItem key={index}>
<Thumbnail square size={80} source={{ uri: 'https://dummyimage.com/80x80/000/fff' }} />
<Body>
<Text>{marker.name}</Text>
<Text note>{marker.dis}</Text>
</Body>
</ListItem>
)})
return result;
}

Redux - What is the point of normalizing the data

I'm struggling with the concept of normalizing the data in Redux.
For example, if we normalize the following data:
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"commenter": {
"id": "2",
"name": "Nicole"
}
}
]
}
to this:
{
result: "123",
entities: {
"articles": {
"123": {
id: "123",
author: "1",
title: "My awesome blog post",
comments: [ "324" ]
}
},
"users": {
"1": { "id": "1", "name": "Paul" },
"2": { "id": "2", "name": "Nicole" }
},
"comments": {
"324": { id: "324", "commenter": "2" }
}
}
}
And in my template, I need to loop over the posts and show the post.comments, for example in Angular -
<ul>
<li *ngFor="let post of posts">
{{post.title}}
<div *ngFor="let post of post.comments">...</div>
</li>
</ul>
I need to do some transformation to get back this structure. Otherwise, I can't display the data in my page, So what is the whole point?
The redux docs have a pretty detailed section on this topic but the main reasons are:
This is a concern for several reasons:
When a piece of data is duplicated in several places, it becomes harder to make sure that it is updated appropriately.
Nested data means that the corresponding reducer logic has to be more nested or more complex. In particular, trying to update a deeply
nested field can become very ugly very fast.
Since immutable data updates require all ancestors in the state tree to be copied and updated as well, and new object references will
cause connected UI components to re-render, an update to a deeply
nested data object could force totally unrelated UI components to
re-render even if the data they're displaying hasn't actually changed.
From personal experience, I can say that the third point can deliver significant performance improvements in some situations.
Since you asked, here's an example of how you might look up the Comment entries for a given Post:
const mapState = (state, ownProps) => {
let currentPost, postComments;
if(ownProps.postId) {
currentPost = state.posts[postId];
if(currentPost) {
const {comments : commentIds} = currentPost;
postComments = commentIds.map(commentId => state.comments[commentId]);
}
}
return {currentPost, postComments};
}
class Post extends React.Component { /* component code here */ }
export default connect(mapState)(Post);
This would then be used as <Post postId={123} />.

Unable to publish a Open Graph Action and I receive a weird response

My code used to work, but now I am not able to publish any actions. Everything seems to be in order but the response I get from Facebook is a JSON list of the last 25 actions I took. I know my action isn't published because I'm watching it live in a browser. I'm also trying to post these actions with a browser.
Here is my code:
https://graph.facebook.com/me/NAMESPACE:ACTION?OBJECT=http://www.example.com/pumpkinpie.html&access_token=TOKEN
The response is 25 of these:
{
"data": [
{
"id": ""****"",
"from": {
"id": ""****"",
"name": "****"
},
"start_time": "2012-05-05T04:56:38+0000",
"end_time": "2012-05-05T04:56:38+0000",
"publish_time": "2012-05-05T04:56:38+0000",
"application": {
"id": ""****"",
"name": ""****"
},
"data": {
"website": {
"id": ""****"",
"url": "****",
"type": "website",
"title": "****"
}
},
"likes": {
"count": 0
},
"comments": {
"count": 0
}
},
of course the ** are actual values...
You cannot do HTTP POST for actions in a browser, only HTTP GET, which is why you receive the list of previous actions.
Please use cURL or the Graph API Explorer to accomplish creating an action.

Categories