I am doing a POC for isomorphic JavaScript application to render HTML from the server side. The POC is working with simple HTML, but I want to make an API call and get the JSON response and send to the render function. I tried various ways but it is not working.
What am I missing? I am very new to React.js.
loadCategoriesFromServer: function() {
var self = this;
// get walking directions from central park to the empire state building
var http = require("http");
url = "api url here";
var request = http.get(url, function (response) {
// data is streamed in chunks from the server
// so we have to handle the "data" event
var buffer = "",
data,
route;
response.on("data", function (chunk) {
buffer += chunk;
});
response.on("end", function (err) {
data = JSON.parse(buffer);
//console.log(data.d);
//console.log(data.d.Items);
self.setState({
categories: data.d.Items
});
});
});
}, // load from server end
getInitialState: function() {
return { categories: [] };
},
componentWillMount: function() {
console.log("calling load categories")
this.loadCategoriesFromServer();
},
render: function () {
//console.log("data");
//console.log(this.state.categories);
var postNodes = this.state.categories.map(function (cat) {
console.log(cat);
});
return (
<div id="table-area">
//i want to paint the data here..
</div>
)
}
});
Fetching inside of component using componentWillMount is not a right place, in case when you need to render server side. You need to somehow move it out form component, and pass actual data as props after it is fetched - for example as #JakeSendar suggested in his answer.
I have some experience doing isomorphic app with React, and the main problem I faced is how to wait until all data would be loaded before first render
As #FakeRainBrigand already mentioned in comments, there is not only one way to do this, and it depends from your requirements.
There is few ways to do build an isomorphic app, the some interesting from my perspective is: https://github.com/webpack/react-starter and http://fluxible.io/
But, the most elegant way to do this, as I figured out for myself - is to organise asynchronous rendering for react components, in particular using RxJS.
In general my application is structured as following:
views - React components without any logic (just a view)
models - Observables with current state (initial data is loaded using superagent, then combined with other models and/or actions results).
In simple case it is something like:
Rx.Observable.defer(fetchData).concat(updatesSubject).shareReplay()
actions(or intents) - Observers used to collects user input, do something, and dispatch action results to subscribers models and/or other actions. In simple case something like:
updatesSubject = new Rx.Subject();
action = new Rx.Subject();
action.switchMap(asyncRequest).subscribe(updatesSubject)
components - Observables(stream of virtual DOM elements) combined from models, other components and actions (I have a note about this, explaining how and why to create Observable React elements with RxJS), also now I am planning to add partial components (tuple from: react component, observables, observers, and properties. partially filled with using DI)
router - component responsible to handling location changes,
in general main feature is to map location changes to stream of virtual DOM elements and meta information. But in details, it is bit more complicated in my case(url generation, active url highlighting, handling scrolls when navigating, also it has possibility of nested routes and multiple views)
All this is assembled together using DI container, in my case similar to angular2 DI container, but a lot simplified for my specific needs.
Components, models and actions are created using DI.
On server side application is like this:
var rootInjector = new Injector();
// setup server specific providers
rootInjector.provide(..., ...)
app.get('/*', function(req,res){
var injector = rootInjector.createChild();
// setup request specific providers
injector.provide(..., ...);
injector.get(Router)
.first()
.subscribe(function(routingResult){
res.render('app', {
title: routingResult.title,
content: React.renderToString(routingResult.content)
});
});
}
and similar on client side:
var rootInjector = new Injector();
// setup server specific providers
// actually this is omitted in my case because default providers are client side
rootInjector.provide(..., ...)
contentElement = document.getElementById('#content');
rootInjector.get(Router)
.subscribe(function(routingResult){
document.title = routingResult.title;
React.render(routingResult.content, contentElement)
});
In comparison to flux, it is more declarative and more powerful way to organise app. And in case of isomorphic app - for me, it looks much better that various hacks with flux. But of course there is drawbacks... - it is more complicated.
Likely later, I will opensource all this, but for now - it is not quite ready to be published.
UPD1:
Original answer is a bit outdated(later I plan to update it), and I have some progress in this area.
Links to code mentioned above, already opensourced:
DI container: di1
Container for react componentns(connecting view to observables and obsrvers): rx-react-container
Starter template, for implementing isomorphic widgets, using RxJS and React, and libraries above: Reactive Widgets
About complete application(work still in progress, and documentation there is not quite good, but in general it should be clear):
Router built especially for isomophic reactive applications router1 and react components to use it router1-react
Application template with router and all libraries mentioned above: router1-app-template
React's renderToString method (for rendering components on the server) is synchronous. Therefore, any sort of async task, such as your api request, will still be pending by the time the component has rendered.
There are a couple of ways you can go about fixing this, depending on whether or not you want to fetch your data on the server or client.
If you choose to fetch the data on the server, first move your api-request logic outside of your component. Then, render your component in the callback, passing the fetched-data as a prop. It would look something like this:
response.on("end", function (err) {
var data = JSON.parse(buffer);
var markup = React.renderToString(Component({categories: data}));
});
Inside your component, you'd be able to access the data via this.props.categories.
The other option is to handle the api request on the client. You would make an AJAX request in componentDidMount, and set the component's state from the fetched data. It would look very similar to what you have now, the key difference being that your request logic would live in componentDidMount (async, called on the client) rather than componentWillMount (not async, called on the server).
You should use superagent, works really good for me, also you are missing the most important part, you should use flux to fetch data from a server, flux is the way that facebook strongly recommended, it's pretty easy to use flux architecture.
Related
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!).
I've started working on EmberJS and I absolutely love it. It does have a learning curve but I believe it has fundamentally very meaningful principles.
My questions is how to make GET and POST calls in Ember JS. I understand that there are models / store, but models (in my opinion) would be to only a particular entity's attributes.
Any thoughts on the following questions would be great.
1. USER A send friend request to USER B. Should there be a "Request"
model? And do I just make a POST request?
2. Some arbitrary data needs to be returned for the page. Not
particularly of a model. Should I still make a model for that?
For use a simple GET request?
3. User needs to update this profile photo. How can the file
to be uploaded, be set as a model attribute?
How should I go about making regular GET and POST calls (if I am to do them at all). Just use jQuery's $.ajax() or is there another way. I also found a service ember-ajax which has extended the $.ajax into a promises style.
Any thoughts would be much appreciated.
Long live EmberJS :)
First option: You can use ember-data. It has customizations such as serializers or adapters.
Second option: You can use addons like ember-ajax.
Our usage is just using jQuery's ajax(). We wrote a service that just wraps jquery.ajax() and use it everywhere in our code. We believe that it gives us a flexibility of writing different kind of queries. We don't have any model of ember-data.
Sample -pseudo- code:
export default Ember.Service.extend({
doPostCall(target, data, options=null){
//consider cloning options with Ember.$.extend
var requestOptions= options || {};
requestOptions.url=target;
requestOptions.type='POST';
requestOptions.data=JSON.stringify(data);
doRemoteCall(requestOptions);
},
doGetCall(target, data=null, options=null){
//consider cloning options with Ember.$.extend
var requestOptions=options || {};
requestOptions.url=target;
requestOptions.type='GET';
requestOptions.data=data;
doRemoteCall(requestOptions);
},
doRemoteCall(requestOptions){
//assign default values in here:
// such as contentType, dataType, withCredentials...
Ember.$.ajax(requestOptions)
.then(function(data) {
Ember.run(null, resolve, data);
}, function(jqXHR , textStatus, errorThrown) {
jqXHR.then = null;
Ember.run(null, reject, jqXHR, textStatus, errorThrown);
});
}
});
PS: By the way, if you need to run your app in server-side (with fastboot), you cannot use jQuery. So use ember-network.
If you are performing CRUD operations over models, ember-data is nice. In most apps, CRUD operations account for ~90% of requests.
There is occasions where an app needs to make requests that not ideal for ember-data, and that is where ember-ajax enters the game. It allows you to do requests like you'd do with just jQuery, with the nice addition that requests are done though a service that has some extension points to allow to customize things like headers that are used app-wide, which is more complex with just raw jquery.
If your app is going to run in fastboot, you should use ember-network, because it works both in the browser and in node, while jquery/ember-ajax does don't.
Current best practise would be ember-fetch. since ember-network is deprecated in favor of ember-fetch.
You can just install ember-fetch addon by running the below command,
ember install ember-fetch
and then just import fetch and you are good to use fetch method.
import fetch from 'fetch';
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return fetch('/my-cool-end-point.json').then(function(response) {
return response.json();
});
}
});
Consider, if you will, an app with a few unique views/states - let's call it a game. You have an overworld screen, a battle screen, a multiplayer interface, and maybe a minigame or two.
For the sake of argument, there isn't a lot of code in common between each view, so it lends itself well to AMD - a central controller/dispatcher, and each game state split into a separate file/view.
dispatcher.core.js
> overworld.view.js
> battle.view.js
> tournament.view.js
> minigame.view.js
Input and key commands get routed to the dispatcher, and trickle down to the current active view, which in turn manipulates the DOM as needed. One-way AMD relationships, so far so good.
The thing I'm getting hung up on is the response flow. The API response data that goes through the system is diverse, often affecting multiple views at the same time. Consider this case:
User presses buttons to move
Key commands gets routed to map view for movement animation
Map sends AJAX request to server for movement result
AJAX returns "battle commence" response to dispatcher
Dispatcher tells map view to disable itself, then battle view to init
The dispatcher was designed for this - to receive instruction and distribute. It seems like the obvious choice, much more than letting views affect each other directly.
However, there's a fundamental flaw here - the one-way relationship between the dispatcher and the views is violated as soon as the AJAX result is sent from the view to the dispatcher. You can either use the dispatcher for your AJAX callback, or you can instruct the dispatcher to make the AJAX call for you - but either way the view requires a way to reference the dispatcher, which as I understand it, violates the core tenet of AMD. For the life of me, I can't figure out how this would be implemented correctly!
My question is this - how would one implement such a structure correctly? Is this a limitation of AMD, or am I misunderstanding it's use on a deeper level?
This question is intended to be for more of the general case, but if it affects answers at all, I'm using Require and jQuery for AMD and AJAX, respectively.
Is this a limitation of AMD, or am I misunderstanding it's use on a deeper level?
AMD does not by any means impose one way relationship between object instances in general. What it does strongly recommend to avoid (because even this is not an absolute requirement) is circular dependencies between modules. And the type of dependencies that matter for AMD are loading dependencies.
You can certainly have a module named dispatcher that goes:
define(function () {
function Dispatcher(views) {
this.views = views;
for (var ix = 0, view; (view = views[ix]); ++ix)
view.init(this);
}
return Dispatcher;
});
And viewA, viewB, that are structured like this:
define(function () {
function View() {
// ...
}
View.prototype.init = function (dispatcher) {
this.dispatcher = dispatcher;
};
// Etc...
return View;
});
Your main module could do:
define(['dispatcher', 'viewA', 'viewB'], function (Dispatcher, ViewA, ViewB) {
var viewA = new ViewA();
var viewB = new ViewB();
var dispatcher = new Dispatcher([viewA, viewB]);
});
The above is meant to be a schematic example of what is possible, not a prescription for a good design. At any rate, the point is that is is perfectly feasible as far as AMD is concerned to have circular references between objects.
There's nothing about AMD that is limiting here; it's entirely about the design of your modules themselves.
A common way to handle this is with an event-emitter.
The dispatcher can call methods directly on a view, but the view emits events which the dispatcher can listen and respond to, removing the need for a circular reference (as the view doesn't care where the events go, so it doesn't require a reference to the dispatcher.)
Fitted to your example workflow, it might look like this:
overworld tracks keypress
overworld animates in response to keypress
overworld emits 'move' event for dispatcher
// overworld.view
this.emit('move', {data});
// dispatcher
overworld.on('move', getMoveResult) // getMoveResult fires AJAX request
response tells dispatcher it's time to battle
dispatcher updates views
overworld.hide()
battle.show()
So I'm working on building a dynamic model for a project that reacts to data sent from an API. The api will return, among other things, what your location should be and this in turn becomes the url. So, eg:
{
location: 'xyz'
(...)
}
So currently my router will transition to the right route dynamically. But I still have to hardcode each route ( IndexRoute, LocationXYZRoute, LocationABCRoute, etc).
My goal is to create a single route that handles things dynamically. We'll call it App.LocationRoute and my routes would look something like:
App.Router.map(function() {
this.resource(':location', function() {
this.route(':subLocation')
}
}
Now, I have two architectural questions:
1) Whats a good way to handle this sort of dynamic routing? (I've read through the guide about dynamic routing using the ':post_id' type example, but I think I need a more holistic example to really grasp it.
2) The API sends back a whole host of other data as well. I want to add this to the route's model but I also have some other static models. Doing...
this.controllerFor(location).set('content', APIdata);
... works, but it does not set for routes currently using static models. I tried something like:
this.controllerFor(location).set('apiData', APIdata);
and...
this.controllerFor(location).set('model:apiData', APIdata);
... but neither worked.
Any suggestions?
1) Yes, you should use dynamic segment
this.resource('location', { path: '/location/:location_id' }, function() {
this.resource('sublocation', { path: '/sublocation/:location_id' });
});
2) Are you using ember-data? You could check sideloaded data. Anyway, you could read the json and set the payload of each entity for each specific route.
this.controllerFor('location').set('content', APIdata.location);
this.controllerFor('user').set('content', APIdata.user);
People could help you better, if you separate your questions and create a http://emberjs.jsbin.com/ with isolated each specific case?
I have an AngularJS application that has a lot of directives which are populated from nested nodes in AJAX requests. For example I might get a response like:
{
name: "Blog post title",
comment_ids: [1, 2, 3]
}
Currently in my controller, I will make a request to load data for each of the child nodes comments and populate them onto the scope. Something like:
_.each(comment_ids, function(id) {
scope.comment_data[id] = $http.get(id); // psuedocode for the $get
});
I then use the data on the page like:
<comment data="comment_data.1"></comment>
This works great but I'm thinking about converting my directives to take an id instead of an object and to handle loading the data themselves. The problem I face is that I might get multiple requests to the same endpoint if the same directive is present on the page multiple times, e.g.
<comment id="1"></comment>
<comment id="1"></comment>
<comment id="1"></comment>
This is going to result in three calls to the comments endpoint. Given this:
Will the browser batch multiple calls to the same HTTP endpoint into one request if it occurs in small space of time, or should I write an interceptor to handle doing it myself?
I would move the $http calls to their own service. The benefit is you can use it in a service or controller as needed and as many times as necessary--this is especially useful if you use any kind of JS-controlled caching.
I'm developing an AngularJS oData service for SharePoint 2010/2013 REST calls that uses localStorage to cache results and only fetches updates from the server. This works especially well when I'm calling the same data over and over, it is just fetched from localStorage without ever making the HTTP call.
// Pseudo code to put in a service
var persistentCache = function (query) {
return localStorage[query] ? {
'then': function (callback) {
callback(JSON.parse(localStorage[query]));
}
} : $http(query).then(function (resp) {
return localStorage[query] = JSON.stringify(resp);
});
};
Alternatively, you could use a module design pattern to cache your calls just for the life of the app and only fetch if it is not in the app cache.
// Pseudo code to put in a service
var appOnlyCache = (function () {
var _cache = {};
return function (query) {
return _cache[query] ? {
'then': function (callback) {
callback(JSON.parse(_cache[query]));
}
} : $http(query).then(function (resp) {
return _cache[query] = JSON.stringify(resp);
});
};
}());
Both examples with more robunst implementation with compression & error handling can be found in the AngularSharepoint service I've working on for an app right now: AngularSharepoint
This problem is now solved quite elegantly with: https://github.com/facebook/dataloader
DataLoader is a generic utility to be used as part of your
application's data fetching layer to provide a consistent API over
various backends and reduce requests to those backends via batching
and caching.