Quote:
Originally Posted by RustyBrooks
Not sure I understand how #3 would work, the rest of it sounds pretty standard
Like say I have 2 components, like, Person and Child. So my JSX for Person is like
Code:
<div>
{childlist.map((x) => <Child name={x}>)}
</div>
For this to work, Child needs to be imported into this JS file. If you move Child, then you have to change the import?
I guess I'd like to see a simple example of it if you have one (with routing, I feel like I am doing routing wrong because it does not work the way I'd like it to)
I'm talking about being able to move, renamed or delete stuff (currently only page-level components) w/o breaking import statements or component references off in other files. I know it seems like a little thing but I think that adds friction to good renaming, reorganizing and refactoring - which ultimately makes for crappier code. For example:
Once I got this system in place, I was able to move all the CustomUI stuff into a subfolder and nothing broke, no import statements had to be updated anywhere. I could also delete the whole folder and the only thing that would break are places the app is linking to or serving the pages.
So it's like the old days where you drop a JSP in a folder and it just works. Except better in that the route doesn't change.
The first step is an npm start script that creates a metadata file of everything in the /routes folder that matches - which is currently *.js but not *.test.js.
This script runs every time you call npm start:
Code:
const glob = require('glob');
const fs = require('fs');
loadRoutes('/src/routes', ['*.test.js']);
function loadRoutes(dir, excludes) {
console.log('pre-start.js: loadRoutes called - dir: ' + dir + ', exclude: ' + excludes);
const routes = [];
glob.sync("**/*.js", { "ignore": excludes, cwd: './src/routes' })
.forEach(function(file) {
console.log('pre-start.js: registered route: ' + file)
routes.push(file);
});
const file = {"IMPORTANT!": "GENERATED FILE - see pre-start.js", routes};
fs.writeFile('src/route-metadata.json', JSON.stringify(file,0,2), function(err){
if(err) console.error(err);
else console.log('pre-start.js: src/route-metadata.json written')
});
}
In my node version of this, that part was all done seamlessly because node has access to the file directory, which client-side JS does not. So the directory scan needs to be done when the react dev server starts. One issue I can see is that deciding whether to check in or just always generate route-metadata.json might get a little weird. I'll see how it plays out.
Here's how it dynamically loads the routes in the index.js file:
Code:
// dynamic routes are calculated by node on npm start - see ../pre-start.js
import routesMetadata from './route-metadata.json';
...
const App = () => {
return (
<BrowserRouter>
<div className="sans-serif">
<Switch>
{routes.map((r) => {
return <Route key={r.route} path={r.route} component={r.default}/>
})}
</Switch>
</div>
</BrowserRouter>
)};
async function doRender () {
routes = await utils.loadRoutes(routesMetadata);
render(<App />, document.getElementById('root'));
}
doRender();
the files are dynamically imported in utils.js:
Code:
loadRoutes: async routesMetadata => {
const routes = [];
for (let routeName of routesMetadata.routes) {
const module = await import('./routes/' + routeName.substr(2));
console.log("registering route: " + module.route);
routes.push(module);
};
return routes.sort(compareRoutes);
function compareRoutes(a, b) {
const indexA = a.routeIndex || 0;
const indexB = b.routeIndex || 0;
// inverse sort
if (indexA > indexB) return 1;
else if (indexA < indexB) return -1;
else return 0;
}
}
As you can see there's a convention that the component name and file name be the same. But we could always export an optional component name property if that couldn't be done for some reason.
I used BrowserRouter because it was the first router I stumbled across. It may be easier or harder with other types of routers. The routes also have optional routeIndex so they can be loaded in order if necessary ('/' has to come last, could be other wildcard matching routes if react supports that). If we get a bunch of routes, we will probably want to push sorting to the node step instead of the browser step. But that gets messy because node has to grep into the files to find out the route index. Currently this is just for my kitchen sink demo app that's basically a collection of stuff the devs can use.
And finally here's what a route component looks like:
Home.js:
Code:
import React, { Component } from 'react';
import { Auth } from 'aws-amplify';
import { withOAuth } from 'aws-amplify-react';
import { utils } from "util.js";
import './Main.css';
export const route = "/";
export const routeIndex = 1000; // optional - higher # indicates route is loaded last (like z-index)
class Home extends Component {
...
}
export default withOAuth(Home);
Most route components only have to export route and not routeIndex. At first I just added route as a static variable on the
Home class. But in this case Home is wrapped in the higher-order-component
withOAuth. There doesn't seem to be any way to get access to the wrapped class. So I had to add the extra export statement that the top.
Also by adding a .env file which points to the /src directory, the routes are able to use absolute path to import 'util.js' - this helps when reorganizing into subfolders.
This is all kinda hacky still - there are probably better ways to do some of the mechanics. But it works at least. I even tested on IE and everything's fine.
I will chip away at it as better ideas come for how to handle different pieces - as I did with the node OS framework over a couple years. By the end that app had some 200 routes, which were much easier to handle w/o import statements all over the place. The front end devs loved it because they could just create their node orchestration component to call our back end scala/play layer, w/o having to touch a bunch of other files.
The system that came after I left was more like a standard node app with a many to many service layer to route handler layer - and import statements scattered all over a router hierarchy. The same front end devs told me they wished they had yukon back. The new system pretty much required a full time node dev, whereas mine didn't.
tl;dr - suzzer likes component-based architecture combined with self-discovery and route handling done internally to the components - not orchestrated from central command.
Last edited by suzzer99; 10-19-2018 at 03:21 PM.