Security News
Node.js EOL Versions CVE Dubbed the "Worst CVE of the Year" by Security Experts
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.
A small framework to alleviate the pain in creating universal React / Redux / React-Router applications.
Creating a universal React /
Redux /
React-Router
application from scratch is still somewhat a pain.
after.js
is almost perfect, but I wanted something that doesn't abandon
redux, or seem like an afterthought that gets tacked on (i.e. I don't like
passing in the redux store into getInitialProps
of the parent containers).
later.js aims to fill the gap (or glue together) of React/Redux/React-Router as separate libraries and the work required to have a complete universal-rendering application. It is built with razzle in mind, so some assumptions are made but it should be somewhat simple to apply to any context.
With after.js in mind, I set out to:
later.js lists all react-*
/ redux
dependencies as peerDependencies, they
must be installed first (with later.js). You can get started even faster with
create-later.js-app!
npm i --save react \
react-dom \
react-helmet \
react-router-dom \
react-router-config \
redux \
react-redux \
later.js
resolveRoute([store], [loadData], [ctx])
asyncComponent
/ code-splittingrender([options])
hydrate([options])
connectLink([Component], [eventHandler], [onError])
StatusConsumer
Routing is provided by react-router v4 and react-router-config.
loadData
DeclarationsA user-provided resolveRoute
function will be called with an
array of the functions/objects that are declared alongside the route. So two
instance types are supported within the loadData property:
Functions
- Will be called with the route context. See
react-router's documentation for more information on the
match object.Any
- Anything that is not a function will be passed through as itself.An example better explains this process. In the following route setup:
[{
path: '/about',
component: About,
loadData: loadAbout,
routes: [{
path: '/about/me',
component: Me,
loadData: [loadAboutMe]
}]
}]
If a user visited /about/me
the provided resolveRoute
method would be used to create a Promise
thats awaited on route changes,
structured as:
Promise.all([
...resolveRoute(store, loadAbout, { match, req, }),
...resolveRoute(store, [loadAboutMe], { match, req, }),
])
It is expected that the store is populated with data and it is not passed into the component.
Note - It is assumed resolveRoute
will return a Promise
.:
resolveRoute([store], [loadData], [ctx])
store
- The redux store created using createStore
.loadData
- The matched loadData match.ctx
- Some request context for loadData
. Including the react-router
match and the request where possible.resolveRoute
is what is behind connecting the store to all of the loadData
properties. And is solely responsible for populating the redux store from the
calls.
asyncComponent
/ code-splittingAn asyncComponent
is provided that makes code-splitting (and loading) super
simple. When declaring a component within the routing setup, just
wrap the provided component in a simple loader function using dynamic imports.
import { asyncComponent } from 'later.js';
const routes = [{
path: '/about',
component: asyncComponent(() => import('./asAbout')),
routes: [{
path: '/about/me',
component: Me,
}]
}]
If a user visited /about/me
the server/client will load the chunk required
for the ./asAbout
page automatically. This automatically hooks up to the
connectLink
method, so that progress can be shown on some
event, rather than through some global progress bar or a placeholder.
render([options])
The render
function makes server-side rendering easier, by building in
data-resolution and loading of asyncComponent
s. It will return an html document
or redirect the user (using the res
option).
options
res
- Express response.req
- Express request.routes
- Routes configuration.assets
- Assets manifest.createStore
- Function that creates a redux store.resolveRoute
- Function that converts loadData
properties to a Promise
.appendToHead
- optional - Function that returns a react-component to
append to the default Document head.renderMethod
- optional - Alternative render method, use this to add
wrapped componentsdocument
- optional - Alternative Document.A simple and shortened example:
import { render } from 'later.js';
import createStore from './createStore';
import resolveRoute from './resolveRoute';
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
...
.get('/*', async (req, res) => {
try {
const html = await render({
req,
res,
routes,
assets,
createStore,
resolveRoute,
});
res.send(html);
} catch (error) {
res.json(error);
}
})
hydrate([options])
The hydrate
function replicates render
on the client. Making it easier to
populate the store/load initial data that was prefetched on the server.
options
routes
- Routes configuration.createStore
- Function to create a redux store.resolveRoute
- Function that converts loadData
properties to a Promise
.hydrateMethod
- Optional method to override the default react hydration. Add
required provider components here.connectLink([Component], [eventHandler], [onError])
connectLink
makes it easy to connect components to the later.js data-loading
and asyncComponent
-loading setup. Wrap a component with connectLink
to
load the data/components before re-routing.
If the specified eventHandler
option exists on the connected component
and it is a function
/Promise
it will call/await the provided handler.
After the handler is done (and it doesn't return/resolve to false
!) it will
route to the to
property that is provided on the Component
.
The onError
function is called when an error occurs. A routeError
is also
passed to the child if an error occurs during the fetch.
import { connectLink } from 'later.js';
const LoadingLink = ({ to, children, onClick, isRouteLoading, routeError}) => (
<a href={to} onClick={onClick}>
{isRouteLoading ? 'loading' : children}
{routeError ? 'error!' : null}
</a>
);
const ConnectedLoadingLink = connectLink(LoadingLink, 'onClick');
...
// In some render method...
<ConnectedLoadingLink to="/about" onClick={isOkToContinue}>
About Me
</ConnectedLoadingLink>
Although connectLink
makes it easy to connect to route changes, there may be
cases where a link/button/event is not causing the route to change! In those
cases later.js will fallback to a global route change handler.
Don't fret, asyncComponent
's will still get loaded and route-data will be
fetched. Want status updates on that global handler? Check out the
StatusConsumer.
StatusConsumer
The <StatusConsumer/>
is a react-context-api consumer that provides an
isRouteLoading
property for use in progress bars/indicators.
import { StatusConsumer } from 'later.js';
const ProgressIndicator = () => (
<StatusConsumer>
{({ isRouteLoading }) => (
{isRouteLoading ? 'loading!' : null}
)}
</StatusConsumer>
);
The document component used to render the page can be replaced. See the Document component provided for what is required though. Make use of react-helmet where possible instead.
We probably shouldn't re-fetch all the data for a route on a parent -> child route transition where the location change does not cause the parent fetches to change. This would reduce a ton of needless requests for some shared state.
FAQs
Setup util for universal react/redux applications
We found that later.js demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.
Security News
cURL and Go security teams are publicly rejecting CVSS as flawed for assessing vulnerabilities and are calling for more accurate, context-aware approaches.
Security News
Bun 1.2 enhances its JavaScript runtime with 90% Node.js compatibility, built-in S3 and Postgres support, HTML Imports, and faster, cloud-first performance.