Best way for dynamic interface, loaded from database, in React - javascript

I need to rewrite a webapp, and I would like to do it in React.
How it currently works is that in the database there's a row for each "page", which could be something like
{
row: 1,
column: 2,
width: 2,
height: 1,
data: 'Why hello there',
componentType: 'Box'
}
So, I have two problems here:
One: I would like to dynamically load a React component ("Box") that I did not know about at run-time.
Two: Is there some obvious way of doing the component/layout switching here? I know about react-routes, but all the tutorials I have seen shows it to be a lot more "static" (where you basically already know all your views and routes before the app starts, and where it doesn't really seem possible to get that data from a database)

The idea here is to render a version of your app that is essentially "blank". Whether it be through a splash screen or loading spinners, or some combination of the two, or something else.
Then after the app mounts, in your componentDidMount method, you do an http request to an API running on a server that fetches data from a database and returns your data in a format your webapp understands (probably JSON). Then you populate your app with the returned data, turning off any loading screens or spinners when it's done.
Check out this example from the React docs.

You can use webpack dynamic bundle loading

Related

Distributable/Distributed Web App with VueJS

I need to build a distributable app using VueJS. I'll describe the scene, so that it can be understood better.
Immagine the following:
Four companies have the same products database
Each of them has it's own politics, prices, custom colors, parameters, all of which are also stored in that database
These 4 companies want to integrate into their relative websites a page which shows their own catalogue (with their custom prices and politics and whatever), as well as, when you click on some of the products, the related information about that product
The question is: how can I make such an app using VueJS? Write it once and distribute it to these 4 different company websites?
And I've started to answer to my question doing the following:
I created an API which can be accessed by API Keys
Assigned to each company its very own API Key, so that requests coming with that API Key are associated with the given company
I created an app using VueCLI (which uses vue-router, for the pages/views, and axios for the API calls as well), wrote all my logic and components and styles and whatever else...and for development purposes I tried to use one of the 4 API keys (hard coding it into the axios.defaults.headers) to see if it all works...and it does!
Now, here comes the problem: I need to build/export the app in such a way that I can do something like this:
In www.company1.com/catalogue
Call catalogue.js and pass it the company1 API key
In www.company2.com/catalogue
Call catalogue.js and pass it the company2 API key
In www.company3.com/catalogue
Call catalogue.js and pass it the company3 API key
... I'll omit the company4 cause i'm sure you already got me.
Obviously I could export 4 different versions with the hard coded 4 different API's...but seems like a pretty stupid idea to me! Also, it exposes my client a lot cause, well, everybody could link to some of the companies' catalogue.js and, without configuration or whatever, visualise on it's own website my clients products..(which is obviously a problem).
I would like to have:
ONLY ONE catalogue.js file with some exposed parameters, as, for example: the API Key needed to identify the company.
In this way, everybody could still use that catalogue.js file, but with wrong credentials, it wouldn't produce a thing (i.e. will get an API error of unrecognized API key).
How on earth am I supposed to do this?
I'm banging my head on the wall from 2 weeks.
I've tried to export the app as Library, get errors like: Unknown custom element: <router-link> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
I tried to export the app as Web Component...got same error and others as well.
Please help me understand where I go wrong, and how should I approach the problem.
Look into Vue Environment Variables https://cli.vuejs.org/guide/mode-and-env.html
Or you can make independent settings.json file and load it through the axios

rendering js server side

I have a webapp that displays a lot of interactive JS data tables and graphs in a single page (about 50-60). The page rendering required takes a lot of time (Chrome WebTools show about 20-25 seconds in the "Rendering" State from ajax response to completion) since each interactive element is a standalone "widget".
Because the types and structure of the tables/graphs is mostly constant and only the data varies, I think it's very inefficient to have the client render them each time. It would be better to send the client a rendered page and only populate the data where needed.
I've read about Headless Chrome and Puppeteer but they are NodeJS modules. I was wondering if there was a way to incorporate this server-side rendering of JS in a Python-based backend.
You can render React components server-side with python-react or with python-react-v8.
Installation:
pip install react
Usage:
from react.render import render_component
rendered = render_component(
'/path/to/component.jsx',
{
'foo': 'bar',
'woz': [1,2,3],
}
)
print(rendered)
More about the render server.

Is it possible to reload the router in a running ember.js app in order to update the path strings of routes?

I'm implementing multi-language support in my app, and I guess this is the last thing that I would need in order to be able to change between languages without reloading the whole app/page. (I already have a solution with full page reload.)
For a simple example let's say this is how my router looks:
Router.map(function() {
this.route('search', { path: t('search') });
this.route('item', { path: `${t('item')}/:id`);
});
The t function would be getting the correct translation for the given strings in the currently active language.
The structure of the route hierarchy won't change, the only things that need to be updated are the path strings. Application state should be kept, of course.
I'm wondering whether this is possible to do.
I am not %100 sure about the correctness of what I wrote but Router.map is executed and resources with the definitions given within this method is transformed to a DSL instance and that is then passed to the actual router maintained by Ember.Router itself. In order to achieve what you want I believe what we need is dynamic modification to the router even if it is just the paths you need yo modify not the whole route structure.
If you look at the following github issue, Ember.js no more supports dynamically adding routes (hence no dynamic modification to the existing ones I believe). With all that said, I believe what you want is not possible without reloading the whole app (hence losing the application state).

React, async loading for multiple sections/views with different components

I'm having an issue thinking about the best way to architect a React app with multiple pages/views (still a SAP).
Let's say we have a simple app with 4 major sections (pages): dashboard, users, stats, comments. Each section has different components in it (think react components). For example, the comments section would have a hierarchy like so:
CommentsSection
- CommentsQueue
-- Comment
--- Text
--- Buttons
- CommentsApproved
--Comment
--- Text
--- Buttons
In a framework like angular for example, the 4 main sections would be split into partials, and loaded in an ng-view upon request, with their respective components inside. When landing on the homepage, the app would only load the dashboard view and upon the user clicking on a nav item, the selected route (i.e. app/users or app/users/:id) would trigger and the app will load the required "template-view-partial" (without a browser refresh).
Now in terms of React, how would this occur? it seems like ALL views and ALL their components would need to be available in a browserified JS file and the app can then update the DOM.
This seems terribly wrong, as we'd be loading all sections in the first load, even if the user doesn't ever need to get to that section. Granted, we could split it with routes on the server, and only serve the components for the page based on the route, but that would require a browser refresh, where as in Angular for example, it would happen without a browser refresh as the view is loaded asynchronously.
The question is, how can this asynchronous loading happen in a React-based app?
I think there's a few different ways in approaching this, I'll explain the approach that I am currently using for my work and side projects.
Instead of using browserify, we use a module-bundler called webpack (https://github.com/webpack/webpack). What's great about webpack is that it's like Browserify but can split your app into multiple 'bundles'. This is great because if we have multiple components/views, the user would just download the features they need for that particular view without having to download everything initially. It allows react-components and their dependencies to be downloaded on demand.
Pete Hunt wrote an article that goes into depth on the benefits of webpack when using it with React (including how to async load react components), and how it is similar/different to Browserify and modern build tools like Grunt/Gulp: https://github.com/petehunt/webpack-howto
I have described one solution using webpack here : http://blog.netgusto.com/asynchronous-reactjs-component-loading-with-webpack/
In essence :
use require.ensure([], cbk) to define code chunks; in the cbk, load your packages synchronously using require()
in your host component, load your asynchronous component in componentWillMount(), and set in in the host component state.
use it in the host component render, when defined on the state

Caching URL view/state with parameters

I'm making a mobile app using Cordova and AngularJS. Currently I have installed ui-router for routing but I'm open to any other alternative for routing.
My desire: I want to cache certain views bound with parameters. In other words I want to cache paths (or pages).
Example situation: let's say that we see some dashboard page, click on some book cover which redirects to the path book/2. This path is being loaded for the first time into app. Router redirects from HomeController to BooksController (whatever the name). Now the BooksController loads data for given $stateParams (book id = 2) and creates view filled with info about chosen book.
What I want in this situation:
I go back to the dashboard page - it is already loaded (cached?)
I choose book #2 again
Controller or router notices that data about this book is already loaded
The view isn't being recreated, instead it's being fetched from cache
Actually, it would be best to cache everything what I visit based on path. Preloading would be cool too.
Reason: performance. When I open some list of books then I want it to show fast. When view is being created every time, then animation of page change looks awful (it's not smooth).
Any help would be appreciated.
EDIT:
First of all, since I believe it's a common problem for many mobile HTML app programmers, I'd like to precise some information:
I'm not looking for hacks but a clear solution if possible.
Data in the views uses AngularJS, so YES, there are things like ng-bind, ng-repeat and so on.
Caching is needed for both data and DOM elements. As far as I know, browser's layout operation is not as expensive as recreating whole DOM tree. And repaint is not what we can omit.
Having separate controllers is a natural thing. Since I could leave without it I cannot imagine how it would work anyway.
I've got some semi-solutions but I'm gonna be strict about my desire.
Solution 1.
Put all views into one file (I may do it using gulp builder) and use ng-show. That's the simplest solution and I don't believe that anyone knowing AngularJS would not think about it.
A nice trick (from #DmitriZaitsev) is to create a helper function to show/hide element based on current location path.
Advantages:
It's easy.
KIND OF preload feature.
Disadvantages:
all views have to be in a single file. Don't ask why it's not convenient.
Since it's all about mobile devices, sometimes I'd like to "clear" memory. The only way I can think of is to remove those children from DOM. Dirty but ok.
I cannot easily cache /book/2 and /book/3 at the same time. I would have to dynamically create DOM children on top of some templates for each view bound with parameters.
Solution 2.
Use Sticky States AND Future States from ui-router-extras which is awesome.
Advantages:
Separated views.
Very clear usage, very simple since it's just a plugin for ui-router.
Can create dynamic substates. So it would be possible to cache book1, book2 but I'm not sure about book/1 and book/2
Disadvantages:
Again, I'm not sure but I didn't found an example with caching a pair/tuple (view, parameters). Other than that it looks cool.
This is precisely the problem I had to solve for my site 33hotels.com. You can check it and play with the tabs "Filter" and "Filter List" (corresponding to different Routes), and see that the View is updated instantly without any delay!
How did I do it? The idea is surprisingly simple - get rid of the Router!
Why? Because the way the Router works is it re-compiles the View upon every single Route change. Yes, Angular does cache the Template but not the compiled View populated with data. Even if data do not change! As the result, when I used the Router in the past, the switch always felt sluggish and non-reactive. Every time I could notice annoying delay, it was a fraction of second but still noticeable.
Now the solution I used? Don't re-compile your Views! Keep them inside your DOM at all times! Then use ng-hide/ng-show to hide/show them depending on the routes:
<div ng-show="routeIs('/dashboard')">
<-- Your template for Dashboard -->
</div>
<div ng-show="routeIs('/book')">
<-- Your template for Book -->
</div>
Then create a function routeIs(string) inside your Controller to test if $location.path() matches string, or begins with string as I am using it. That way I still get my View for all pathes like /book/2. Here is the function I am using:
$scope.routeBegins = function () {
return _.some(arguments, function (string) {
return 0 === $location.path().indexOf(string);
});
};
So no need to be smart with caching - just keep it in the DOM. It will cache your Views for you!
And the best part is - whenever your data is changed, Angular will instantly update all the Views inside your DOM, even the hidden ones!
Why is this awesome? Because, as user, I don't have to wait for all the parsing and compiling at the moment I want to see the result. I want to click the tab and see my results immediately! Why should the site wait for me to click it and then begin all the re-compiling as I am waiting? Especially when this could be easily done before, during the time my computer is idle.
Is there any downside? The only real one I can think of is loading memory with more DOM elements. However, this actual byte size of my views is negligible, comparing e.g. with all JS, CSS and images.
Another possible but avoidable downside is the re-compilation cost of the hidden views. This is where you can get smart and avoid computation-heavy parts depending on the current routes.
Also, you are not re-compiling the whole View, just the parts affected by data changes, which also lowers computational cost.
I find it quite remarkable that everyone is using Routes and seems to be completely unaware (or ignorant) of this problem.
1) About static pages in the app (views), angular takes care of loading them.
for example: for your dashboard page you need not worry about caching the page, as angular will take care of it. Angular will only load the dashboard view once and on all next requests for the dashboard view, angular will just show you the view(not load the file for view), if it is all a static view without any data loaded by ajax calls.
2) if your dashboard is itself loading the book list(or similar data) via ajax, then you can tell your controller to only load the data once and store it to localstorage and on subsequent requests to the dashboard page can only load the data from the localStorage.
3) similar approach can be used when your BooksController loads the data into a view. You can check in your BooksController if the request for a particular book is been previously made and if not your can store it to localstorage or database. and if the data was previously requested then you can load the same data from the storage without making a request to server.
Example situation:
say user makes request for book1, then
your controller i.e BooksController check whether the same data was requested before,
if not, you can load the data via the ajax call from server and also save it to local storage.
if it was loaded before you will load the data stored in the localstorage or in the database.
If you're using ui.router, then you should take a look at ui.router extras, specifically the sticky states module. This allows you to cache the state 'tree' (including rendered views) so they don't have to be compiled or re-rendered on state changes.
http://christopherthielen.github.io/ui-router-extras/
Here's a demo:
http://christopherthielen.github.io/ui-router-extras/example/sticky/#/

Categories