Map JSON Object To Something Worthwhile in React - javascript

I was wondering if I could get a little advice on pulling in some JSON data in my React app. I have a good amount of experience in front end development, but unfortunately have minimal knowledge when it comes to pulling in data from remote resources.
Here is my sample json data
{
"hello": {
"yo" : "random",
"hey" : "cool"
}
}
This data sits locally and I can pull it in just fine, but I am wondering if there is an easier way to get the data into the format I want. My code is below of what I am doing,
grabData() {
axios.get('data.json')
.then(function(response) {
var info = response.data.hello;
info = Object.values(info);
this.setState({ info : info })
});
}
For those unfamiliar, I am using an ES6 feature to turn the object into an array so I can use the map method in a child component that will receive this array.
However, I think this method is flawed, as it completely strips the object of its keys, which I would like to keep intact to use in the map method. Any help would be greatly appreciated.
PS. Is it really necessary for me to use "response.data.hello" to access the object?

Related

Where to store remote data in Svelte for responsive data vizualisations with Svelte and D3 (best practices)

Disclaimer: I am really not well experienced with neither svelte, nor D3, nor general JavaScript patterns. But I really like it, so I really want to learn it and already invested quite some time. Still, this feels like a super basic question that annoys me a lot. I hope it is not too confusing and someone might has an idea.
It is basically about how to setup a simple graph (let it be a bar chart) in an efficient, reproducible and "best-practice" way. I guess my main concern is on how to pass around the data and use it for different tasks. E.g. I think it might be a good idea to separate out the construction of the scales (using d3) in a separate component. However, this component needs access to the data (and probably also access to the, in the best case resposive width, of the chart-container).
However, also the bars, which are in another component, need access to the data in order to know how do draw the rectangles.
A general misunderstanding (i guess that is the right word) I have with JavaScript is that I do not understand how to fetch data asynchronously (using e.g. the browsers fetchor D3's csvmethod). I simply can not fetch the data and then pass it as prop to another component. Because what I would be passing would be a promise...
So I have this very basic REPL that kind of shows a bit this know I have in my head: https://svelte.dev/repl/398f4c21b7a9409a9811fd8e38703a36?version=3.44.1
It looks like this. In the App.html I fetch the data that I want to use for multiple purposes. However I cannot "get it out" of that component.
<script>
import Chart from "./Chart.svelte"
const url = "https://api.github.com/search/repositories?q=stars:>100000";
async function getData(){
let response = await fetch(url)
let data = await response.json()
console.log(data)
}
//async function getDataLocal(){
// let data = await d3.csv(<path_to_data>)
// return await data
// }
let data = await getData()
</script>
<Chart {data}>Do Something with the data. Make the chart, build the scales, ....</Chart>
So the main questions are:
Are there any ressources on how to learn building sustainable graphics with remote data, svelte and a bit of D3. I already watched many many youtube videos and I guess I will rewatch the one from Matthias Stahl;)
Is it a good idea to use stores in such a case to store the data
And a little more specific: As the data is (probably) fixed, however the dimension arent't: What is a good way/place to let the app know to recalculate the scales etc.
There are 3 separate concerns here:
fetching, storing and retrieving data (aka the data source layer)
manipulating/transforming data (aka the business logic layer)
displaying data (aka the presentation layer)
I will leave the last part aside as it solely concerns D3 (if that is your visualization library of choice) and there are plenty of resources available online on this topic, and I will instead focus on what seems to be the heart of your question, i.e. how to fetch data in Svelte, where to store it, how to pass it around to components, and how to manipulate the data.
1. Asynchronous queries in Svelte
Your first inquiry is about how to deal with asynchronous requests. You cannot use await statements at the root level of the <script> section of a Svelte file, meaning the following reactive statement would generate an error:
// will fail
$: data = await getData(url)
However, you can call an asynchronous function that will handle the assignment. Reactivity will still work and your component will re-render when the url is changed and the new data retrieved:
// will work
$: updateData(url)
async function updateData(url) {
data = await getData(url)
}
Here is a working example based on the REPL in your question
2. Using stores
As you could see from the above example, you had to pass the data to your <Header> and <Chart> components for it to be used in either:
<Header {data}>GitHub Lookup</Header>
<Chart {data}/>
But what if you want to use your Chart somewhere else in your application? What if you have another component that wants to make use of the same data?
Obviously you do not want to fetch the same data over & over (unless the request itself has changed). You also want to avoid passing the data around as a prop everywhere in your app. You will want to make the data available only to these components that will use it.
This is where stores come in handy. Stores can be subscribed to by any component. A writable store will allow its contents to be updated, while a readable store will be -as the name implies- read-only.
A store need not be complex. The following is a very basic writable store:
import { writable } from 'svelte/store'
export const githubStore = writable(null) // initialized with a null value
All you have to do then is interact with your store.
Updating the store in your App component:
import { githubStore as data } from './githubStore.js' // import the store we defined above under the name 'data'
.
.
.
async function updateData(url) {
$data = await getData(url) // using the $ shorthand to access the store (assigning a new value will update the store content)
}
Using (i.e. subscribing to) the store in your components:
import { githubStore as data } from './githubStore.js' // import the store we defined above under the name 'data'
.
.
.
// using the $ shorthand to access the store
{#each $data.items as item (item.id)}
<li><a href={item.html_url}>{item.full_name}</a> [{item.stargazers_count}⭐]</li>
{/each}
Read here for details on using the $ reactive syntax with stores
Now that your child components are subscribing to the store where you stored your data, you do not need to pass that data as a prop any more:
<Header>GitHub Lookup</Header>
<Chart />
Here is an updated version of the REPL above, using stores
3. Further considerations
When you want to start manipulating or transforming data that has been put into a store, derived stores come in handy. When the data in your original store is updated, the derived store will automatically update itself based on the changes to the original.
You can also build on the provided readable/writable stores by adding your own functionality and custom methods. These are slightly more advanced topics but would come in handy where data manipulation is concerned.
Finally, D3 will provide its own data manipulation methods, so it will be up to you to decide how much manipulation you handle directly in Svelte, and how much you delegate to D3. I would probably leave everything connected to visualization (scaling, zooming, etc.) on the D3 side, and have the actual pre-visualization manipulation of data (i.e. the business logic) on the Svelte side (or better yet, directly on the back-end if you have access to that!).

Created a new object, then put an array after it in a loop to patch a property, it works but I dont know why?

I'm working with JavaScript in Node.js and using Express with some mongoose and following a well known RESTful API tutorial on youtube, I've come to patching the API and have been trying to understand why the following code works for sometime now;
updateItem = {};
for (const changes of req.body) {
updateItem[changes.propName] = changes.value;
}
Product.updateOne({ _id: id }, { $set: updateItem })
The rest is just your standard .then().catch() to send the response status, but I'm lost on how creating the object then placing it before an array works to update a value.
It's my current understanding that the object must be instantiated before use, I couldn't just put brackets there and have it work, even if I wasn't using it to set something later. Then I loop through the changes from the request body which must be an array to allow looping, but here's where I get lost.
Does the array of iterated prop names changes.propName get placed inside the updateItem object which is then set to the changed values from the array of properties that are being changed? Do I need to understand $set syntax more? I'm struggling to pick it apart to make it longer or simpler but better to understand.
This is the json array setup I'm passing for testing through postman if it helps;
[
{
"propName": "name", "value": "placeholder user"
}
]
I was unable to find anything to help me understand the interactions going on here, I haven't seen something like this before either but please redirect me if this has already been asked.

Meteor: Data from External API call not rendering

I am relatively new to Meteor, and I'm trying to create a web store for my sister-in-law that takes data from her existing Etsy store and puts a custom skin on it. I've defined all of my Meteor.methods to retrieve the data, and I've proofed the data with a series of console.log statements... So, the data is there, but it won't render on the screen. Here is an example of some of the code on the server side:
Meteor.methods({
...
'getShopSections': function() {
this.unblock();
var URL = baseURL + "/sections?api_key="+apiKey;
var response = Meteor.http.get(URL).data.results;
return response;
}
...
});
This method returns an array of Object. A sample bit of JSON string from one of the returned Objects from the array:
{
active_listing_count: 20,
rank: 2,
shop_section_id: 1******0,
title: "Example Title",
user_id: 2******7
}
After fetching this data without a hitch, I was ready to make the call from the client side, and I tried and failed in several different ways before a Google search landed me at this tutorial here: https://dzone.com/articles/integrating-external-apis-your
On the client side, I have a nav.js file with the following bit of code, adapted from the above tutorial:
Template.nav.rendered = function() {
Meteor.call('getShopSections', function(err, res) {
Session.set('sections', res);
return res;
});
};
Template.nav.helpers({
category: function() {
var sections = Session.get('sections');
return sections;
}
});
And a sample call from inside my nav.html template...
<ul>
{{#each category}}
<li>{{category.title}}</li>
{{/each}}
</ul>
So, there's a few things going on here that I'm unsure of. First and foremost, the DOM is not rendering any of the category.title String despite showing the appropriate number of li placeholders. Secondly, before I followed the above tutorial, I didn't define a Session variable. Considering that the list of shop categories should remain static once the template is loaded, I didn't think it was necessary from what I understand about Session variables... but for some reason this was the difference between the template displaying a single empty <li> tag versus a number of empty <li>'s equal to category.length --- so, even though I can't comprehend why the Session variable is needed in this instance, it did bring me one perceived step closer to my goal... I have tried a number of console.log statements on the client side, and I am 100% sure the data is defined and available, but when I check the source code in my Developer Tools window, the DOM just shows a number of empty li brackets.
Can any Meteor gurus explain why 1) the DOM is not rendering any of the titles, and 2) if the Session variable indeed necessary? Please let me know if more information is needed, and I'll be very happy to provide it. Thanks!
You set the data context when you use #each, so simply use:
<li>{{title}}</li>
If a Session is the right type of reactive variable to use here or not is hard to determine without knowing what you are doing but my rough guess is that a Mini Mongo collection may be better suited for what it appears you are doing.
To get you started on deciding the correct type of reactive variable to use for this head over to the full Meteor documentation and investigate: collections, sessions, and reactive vars.
Edit: To step back and clarify a bit, a Template helper is called a reactive computation. Reactive computations inside of helpers will only execute if they are used in their respective templates AND if you use a reactive variable inside of the computation. There are multiple types of reactive variable, each with their own attributes. Your code likely didn't work at all before you used Session because you were not using a reactive variable.

Ember JS transformation into Ember Data from using promises

I have a deep jSon structure to deal with so I currently implement promises directly into the Ember Model (not dependant on Ember Data).
This is shown as follows: -
return Ember.$.getJSON('/ProcessManager/manage type=submitters&action=getSubmitters').then(function(data)
{
var submitters = [];
$.each(data, function(i, item) {
$.each(item, function(i, item) {
$.each(item, function(i, item) {
Push each submitter into submitter array
submitters.push(item);
});
});
});
return submitters;
});
An example complete jSON response from this URL is as follows: -
{"submitters":{"signsubmitter.jar":{"SignSubmitter":{"description":null,"name":"com.form.custom.submitters.SignSubmitter","jarName":"signsubmitter.jar"}},"custom-classes.jar":{"OutputDirSubmitter":{"description":"Writes
the XML to a
directory.","name":"com.form.custom.submitters.OutputDirSubmitter","jarName":"custom-classes.jar"},"XMLResponseSubmitter":{"description":"Returns
the XML file to the
client.","name":"com.form.custom.submitters.XMLResponseSubmitter","jarName":"custom-classes.jar"},"ChainProcess":{"description":"Chain
the output file to another
process.","name":"com.form.custom.submitters.ChainProcess","jarName":"custom-classes.jar"}}},"success":true}
I have read this URL: http://emberjs.com/guides/models/connecting-to-an-http-server/
I would like to know people's opinion on transitioning to Ember Data with this type of data.
Thank you.
I've been using ember data with deeply nested record sets for a while now and I like it a lot. It should work just fine based on my understanding of the info you provided. Ideally you should be able to define the JSON in the format that ember expects see side loaded relationships here. If you get this right then most things are fine. Initially I spent many frustrated hours due to wrong casing and much of the doco is outdated and shows incorrect casing - this is something that you will have to work out for yourself - but if you get the odd meaningless error then casing may be the issue.
If you are unable to change the server JSON then you can simply override the RESTAdapter and RESTSerializer to meet requirements. This is also simple and works.
Ember data works well for me and does what I need it to.
Ember is designed to work with promises and to load deeply nested record sets the order in which these promises resolve can be very important. There is enough info here on SO on how this can be achieved.

breezejs: adding referential constraint to an entity type

This is a follow-up question to my previous issue - this one was getting a bit messy and is more related to the Telerik Data Service.
The metadata I receive from the server are missing the referential constraints in the association node, although I've set the foreign key attribute on my model.
Therefore I was thinking about manually adding these constraints to my entities in the callback of FetchMetadata.
Is that possible and can someone provide a simple example on how to do it ?
[EDIT]
Here's what I have so far:
manager.fetchMetadata().then(function () {
var mandatType = manager.metadataStore.getEntityType("Mandate");
mandatType.autogeneratedKeyType = breeze.AutoGeneratedKeyType.Identity;
var openPositionsProp = new breeze.NavigationProperty({
name: "OpenPositions",
entityTypeName: "OpenPositions:#DirectDebitModel",
isScalar: true,
associationName: "OpenPosition_Mandate_Mandate_OpenPositions",
foreignKeyNames: ["Id"]
});
mandatType.addProperty(openPositionsProp);
});
But it raises the exception:
The 'Mandate:#DirectDebitModel' EntityType has already been added to a MetadataStore and therefore no additional properties may be added to it.
Ok, I have a possible approach that you might be able to use right now.
Fetch the metadata from Teleriks OData feed just like you do now.
Export the metadataStore created as a result of the previous step via the MetadataStore.exportMetadata method. This will return "stringified" json for the same metadata in Breeze's native format. This format is much easier to work with.
Convert this string to json via JSON.parse.
Modify the json to add referential constraint information. See Breeze Native Metadata format docs here
Create a new MetadataStore and import the modified json into it.
Create a new EntityManager with this MetadataStore and use it. This EntityManager should now have complete Breeze metadata for use with the rest of the feed.
Hope this makes sense!
We are planning on releasing a form of hybrid metadata in the next release. Unfortunately, it doesn't cover your case because we are focusing on how to add custom metadata to an existing metadataStore, and not actually edit/modify the existing metadata.
Another alternative is that we (IdeaBlade) do offer consulting for this type of work. We could probably write a tool that does steps 1 thru 6 for you. Please contact breeze#ideablade.com if this is of interest and mention this post.
So you are getting meta data but it doesn't have a relationship between the entities. Hmm I have not gotten metaData AND tried to create additional model properties that are related.
Your best bet is to add a property that is a navigation type on the constructor.
http://www.breezejs.com/sites/all/apidocs/classes/EntityType.html#method_addProperty
If it were me, I would try it this way (or something similar) inside of the constructor -
myEntity.addProperty({
associatedEntity: {
entityTypeName: "AssociatedEntity", isScalar: true,
associationName: "AssociatedEntity_MyEntitys", foreignKeyNames: ["associatedEntityId"]
}
});
Where myEntity is the name of the current entity, AssociatedEntity would be your navigation property, the associatedEntityId is a property of myEntity that refers to the other entity. Of course to have this be a two-way relationship you would need to add a property to AssociatedEntity as well.
associatedEntity.addProperty({
myEntitys: {
entityTypeName: "MyEntity", isScalar: true,
associationName: "AssociatedEntity_MyEntitys", foreignKeyNames: ["myEntityId"]
}
});

Categories