Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Centarius has same API as After.js.
If you have familiarize yourself with After, then you are not finding it difficult to migrate to Centarius.
Also : You can build it on your SSR boilerplate (either it webpack, parcel, etc).
Centarius is just another component wrapper to ease React SSR.
curl https://codeload.github.com/rayandrews/centarius/tar.gz/master | tar -xz --strip=2 centarius-master/examples/basic
cd basic
After is awesome library but it has some drawbacks that I found it difficult to modify it in my other projects such as,
How about Rogue?
Brilliant Idea to walk into react tree component recursively like Apollo did in their library
But.. something is bothering me..
Table of Contents
Centarius will walk through your React Tree to find static method that you've already specified.
If Centarius not found any static method, Centarius will gracefully return your rendered component.
For now, in all components that you want to fetch, you can add a static async getInitialProps
or another function's name that exactly does the same
.
This will be called on both initial server render, and then client mounts.
// Home.js
import React from 'react';
import { NavLink } from 'react-router-dom';
class Home extends React.Component {
static async getInitialProps({ req, res, match }) {
const stuff = await CallMyApi();
return { stuff }; // returned value from static method not passed on props by default
}
render() {
return (
<div>
<NavLink to="/about">About</NavLink>
<h1>Home</h1>
</div>
);
}
}
export default Home;
getInitialProps | { name } : (ctx) => object
Within getInitialProps
or another function name
, you will get access to all you need to fetch data on both
the client and the server (same like After)
req?: Request
: (server-only) A Express.js request objectres?: Request
: (server-only) An Express.js response objectmatch
: React Router 4's match
object.history
: React Router 4's history
object.location
: (client-only) React Router 4's location
object.isServer
: Check whether code is running on server or clientYou can also add another variable to be passed into static method like Redux Store, etc.
If you are using some server only modules inside
getInitialProps
oranoher function name
, make sure to import them properly. Otherwise, it'll slow down your app.
Taken from Next
data: any
- Whatever you have returned in getInitialProps
or another function name
loading: boolean
- Loading state while fetching data in clienterror: boolean
- Error state (but not throwing error) while fetching data in clientprefetch: (pathname: string) => void
- Imperatively prefetch and cache data for a path.// Home.js
import React from 'react';
import { NavLink } from 'react-router-dom';
import { CentariusConsumer } from 'centarius/core';
class Home extends React.Component {
static async getInitialProps({ req, res, match }) {
const stuff = await CallMyApi();
return { stuff };
}
render() {
return (
<div>
<NavLink to="/about">About</NavLink>
<h1>Home</h1>
<CentariusConsumer>
{({ data, loading }) => (
if(loading) return <div>'Loading...'</div>;
return data.stuff;
)}
</CentariusConsumer>
</div>
);
}
}
export default Home;
Using centariusHoc
to wrap your context into props, just like Redux connect
.
You can also pass options like LoadingComponent
and ErrorComponent
to reduce boilerplate in your render function.
centariusHoc : ({ LoadingComponent?: null, ErrorComponent?: null }) => (Component) => WrappedComponent
TL;DR All static methods will be hoisted
// Home.js
import React from 'react';
import { NavLink } from 'react-router-dom';
import centariusStateHoc from '@centarius/state-hoc'
class Home extends React.Component {
static async getInitialProps({ req, res, match }) {
const stuff = await CallMyApi();
return { stuff };
}
render() {
return (
<div>
<NavLink to="/about">About</NavLink>
<h1>Home</h1>
<div>{this.props.data.stuff}</div>
</div>
);
}
}
export default centariusHoc({
LoadingComponent: () => <div>Loading...</div>,
ErrorComponent: () => <div>Error!</div>
})(Home);
React Router 4 is used in all over Centarius API.
Centarius does not need any router config, so just passing React component with React Router 4 in it, and you're done!
Centarius has default options as follows
{
document = DefaultCentariusDocument,
staticMethod = 'getInitialProps',
rootId = 'root',
dataId = 'server-app-state',
}
Example
Centarius : ({ component, data, options }) => RenderedComponent
// client.js
import React from 'react';
import { hydrate } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './client.css';
import { Centarius } from 'centarius/core';
import { getSsrData } from 'centarius/client';
import App from './App';
const data = getSsrData();
const options = {
staticMethod: 'fetchData', // * change the method to make client can preload data
// Anything else you add here will be made available
// within static method in client
// e.g a redux store, etc.
}
hydrate(
<BrowserRouter>
<Centarius component={App} data={data} options={options} />
</BrowserRouter>,
document.getElementById('root')
);
if (module.hot) {
module.hot.accept();
}
render : (component = App, routerContext, options) => html : string
// server.js
import express from 'express';
import { render } from 'centarius/server';
import App from './App';
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
const server = express();
server
.disable('x-powered-by')
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
.get('/*', async (req, res) => {
const routerContext = {};
if (req.url.match(/.map$/)) return;
try {
const html = await render(App, routerContext, {
req,
res,
assets,
staticMethod: 'fetchData',
customThing: 'thing',
// Anything else you add here will be made available
// within static method in server
// e.g a redux store, etc.
});
res.send(html);
} catch (error) {
res.json(error);
}
});
export default server;
Centarius does not defining any code splitting method like After, Next, or Rogue (with loadable-components) did.
But Centarius does enforce you to implement code splitting with other libraries
With the right custom render function, you can implement it with another React code splitting library out there such as
Centarius works like After and Next, you can override any html structure that suitable for your needs.
Why we need it?
It really helps if you want to add CSS or other component with side-effects (React Helmet, etc) that needs custom document structure.
Example with React Native Web
import React, { Component } from 'react';
import { AppRegistry } from 'react-native';
import { renderToStaticMarkup } from 'react-dom/server';
import { CentariusRoot, CentariusData } from 'centarius/document';
/* eslint-disable */
export default class CustomDocument extends Component {
static async getInitialProps({ assets, data, renderPage }) {
const page = await renderPage();
AppRegistry.registerComponent('CentariusRoot', () => CentariusRoot);
const { getStyleElement } = AppRegistry.getApplication('CentariusRoot', {});
const rnwCss = renderToStaticMarkup(getStyleElement());
return { assets, data, rnwCss, ...page };
}
render() {
const {
rootId,
dataId,
helmet,
assets,
data,
rnwCss,
} = this.props;
const htmlAttrs = helmet.htmlAttributes.toComponent();
const bodyAttrs = helmet.bodyAttributes.toComponent();
return (
<html lang="en" {...htmlAttrs}>
<head>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{helmet.title.toComponent()}
{helmet.meta.toComponent()}
{helmet.link.toComponent()}
{assets.client.css && (
<link rel="stylesheet" href={assets.client.css} />
)}
<style
id="react-native-stylesheet"
dangerouslySetInnerHTML={{
__html: rnwCss
.replace(/<\/style>/g, '')
.replace(/<style id="react-native-stylesheet">/g, ''),
}}
/>
</head>
<body {...bodyAttrs}>
<CentariusRoot id={rootId} />
<CentariusData id={dataId} data={data} />
<script
type="text/javascript"
src={assets.client.js}
crossOrigin="anonymous"
/>
</body>
</html>
);
}
}
If you were using something like styled-components
, and you need to wrap you entire app with some sort of additional provider or function, you can do this with renderPage()
.
Taken from After
// Document.js
import React, { Component } from 'react';
import { ServerStyleSheet } from 'styled-components'
import { renderToStaticMarkup } from 'react-dom/server';
import { CentariusRoot, CentariusData } from 'centarius/document';
export default class CustomDocument extends Component {
static async getInitialProps({ assets, data, renderPage }) {
const sheet = new ServerStyleSheet();
const page = await renderPage(App => props => sheet.collectStyles(<App {...props} />));
const styleTags = sheet.getStyleElement();
return { assets, data, ...page, styleTags };
}
render() {
const {
rootId,
dataId,
helmet,
assets,
data,
styleTags,
} = this.props;
const htmlAttrs = helmet.htmlAttributes.toComponent();
const bodyAttrs = helmet.bodyAttributes.toComponent();
return (
<html lang="en" {...htmlAttrs}>
<head>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{helmet.title.toComponent()}
{helmet.meta.toComponent()}
{helmet.link.toComponent()}
{styleTags}
</head>
<body {...bodyAttrs}>
<CentariusRoot id={rootId} />
<CentariusData id={dataId} data={data} />
<script
type="text/javascript"
src={assets.client.js}
defer
crossOrigin="anonymous"
/>
</body>
</html>
);
}
To use custom document, you need to pass it on server file
// server.js
import express from 'express';
import { render } from 'centarius/server';
import App from './App';
import Doc from './Document';
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
const server = express();
server
.disable('x-powered-by')
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
.get('/*', async (req, res) => {
const routerContext = {};
if (req.url.match(/.map$/)) return;
try {
const html = await render(App, routerContext, {
req,
res,
assets,
staticMethod: 'fetchData',
customThing: 'thing',
document: Doc,
// Anything else you add here will be made available
// within static method in server
// e.g a redux store, etc.
});
res.send(html);
} catch (error) {
res.json(error);
}
});
export default server;
You can provide a custom (potentially async) rendering function as an option to Centarius render
function, just like After.js.
If it presents, it will be used instead of the default ReactDOMServer renderToString function.
It has to return an object of shape { html : string!, ...otherProps }
, in which html
will be used as the rendered string.
otherProps
will be passed as props to the rendered Document.
defaultRenderer = (node) => ({ html: ReactDOMServer.renderToString(node) })
Example
// server.js
import express from 'express';
import { render } from 'centarius/server';
import { Capture } from 'react-loadable';
import { getBundles } from 'react-loadable/webpack';
import stats from 'build/react-loadable.json';
import configureStore from 'store/configureStore';
import App from './App';
import Doc from './Document';
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
const server = express();
server
.disable('x-powered-by')
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
.get('/*', async (req, res) => {
const routerContext = {};
if (req.url.match(/.map$/)) return;
try {
const preloadedState = {};
const store = configureStore(preloadedState);
const modules = [];
const customRenderer = (node) => {
const CustomApp = (
<Capture report={(moduleName) => modules.push(moduleName)}>
<Provider store={store}>{node}</Provider>
</Capture>
);
const bundles = getBundles(stats, modules);
const chunks = bundles.filter((bundle) => bundle.file.endsWith('.js'));
return {
chunks,
store, // notice that this will passed into document
html: renderToString(CustomApp),
};
};
const html = await render(App, routerContext, {
req,
res,
assets,
staticMethod: 'fetchData',
customThing: 'thing',
document: Doc,
store, // this will be passed in static method in server
// Anything else you add here will be made available
// within static method in server
// e.g a redux store, etc.
});
res.send(html);
} catch (error) {
res.json(error);
}
});
export default server;
This project is licensed under the MIT License - see the LICENSE.md file for details
FAQs
♥ Sweet React SSR for Everyone ♥
The npm package centarius receives a total of 14 weekly downloads. As such, centarius popularity was classified as not popular.
We found that centarius demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers 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.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.