Skip to content

Commit

Permalink
pre-fetch data working solution
Browse files Browse the repository at this point in the history
  • Loading branch information
marlon-wiprud committed Jan 25, 2019
1 parent eba6967 commit dd45617
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 39 deletions.
20 changes: 20 additions & 0 deletions lib/api/getRibbitStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createStore, applyMiddleware } from 'redux';

const getRibbitStore = (combinedReducers, ...middleware) => {
let store;
// check if rendering is happening on the client or the server.

if (typeof window !== 'undefined') {
// get state from window
const preloadedState = window.RIBBIT_PRELOADED_STATE;
delete window.RIBBIT_PRELOADED_STATE;

// pass state into redux store
store = createStore(combinedReducers, preloadedState, applyMiddleware(...middleware));
} else {
store = createStore(combinedReducers, applyMiddleware(...middleware));
}
return store;
};

export default getRibbitStore;
13 changes: 13 additions & 0 deletions lib/api/preloadActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function preloadPush() {
fetch('http://localhost:5000/preload-push')
.then(() => console.log('preload push'))
.catch(err => console.log(err));
}

function preloadPop() {
fetch('http://localhost:5000/preload-pop')
.then(() => console.log('preload pop'))
.catch(err => console.log(err));
}

module.exports = { preloadPush, preloadPop };
23 changes: 23 additions & 0 deletions lib/api/ribbitPreload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const { preloadPush } = require('./preloadActions');

const ribbitRequestFactory = () => {
const fetched = {};

function ribbitPreload(cb, component) {
// only perform fetch if running on the server
if (typeof window === 'undefined') {
// dont let component continue to send requests after initial preload
if (!fetched[component]) {
fetched[component] = true;
cb();
preloadPush();
}
}
}

return ribbitPreload;
};

const ribbitPreload = ribbitRequestFactory();

export default ribbitPreload;
15 changes: 15 additions & 0 deletions lib/api/ribbitStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { preloadPop } = require('./preloadActions');
// re-render page with updated state on the server
const ribbitStore = routes => {
routes.forEach(route => {
fetch(`http://localhost:5000${route}`)
.then(response => response.json())
.then(() => {
console.log('Re-render success!');
preloadPop();
})
.catch(err => console.log(err));
});
};

export default ribbitStore;
16 changes: 7 additions & 9 deletions lib/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ function build() {
{
cwd: __dirname
},
() => {}
() => {
const TEMP_STATIC_FILE_FOLDER = `${__dirname}/../../dist`;
rimraf(TEMP_STATIC_FILE_FOLDER, error => {
if (error) process.stdout(error);
process.kill(process.pid, 'SIGINT');
});
}
);

child.stdout.on('data', data => {
Expand All @@ -18,14 +24,6 @@ function build() {
process.stdout.write(data);
});

child.stdout.on('finish', () => {
const TEMP_STATIC_FILE_FOLDER = `${__dirname}/../../dist`;
rimraf(TEMP_STATIC_FILE_FOLDER, error => {
if (error) process.stdout(error);
process.kill(process.pid, 'SIGINT');
});
});

child.stderr.on('exit', data => {
console.log('Error starting server: ', data);
});
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
"babel-plugin-import-inspector": "^2.0.0",
"body-parser": "^1.18.3",
"chalk": "^2.4.2",
"commander": "^2.19.0",
"css-loader": "^2.1.0",
Expand All @@ -44,7 +45,9 @@
"isomorphic-fetch": "^2.2.1",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-redux": "^6.0.0",
"react-router-dom": "^4.3.1",
"redux": "^4.0.1",
"rimraf": "^2.6.3",
"webpack": "^4.28.4",
"webpack-cli": "^3.2.1"
Expand Down
13 changes: 10 additions & 3 deletions server/controllers/htmlTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { StringDecoder } = require('string_decoder');
const decoder = new StringDecoder('utf8');

function htmlTemplate(req, res, next) {
const { appParentDirectory, componentRoute, jsx } = res.locals;
const { appParentDirectory, componentRoute, jsx, preLoadedState } = res.locals;
const ribbitConfig = require(path.join(appParentDirectory, '/ribbit.config.js'));

const reactStream = renderToNodeStream(jsx);
Expand All @@ -14,6 +14,8 @@ function htmlTemplate(req, res, next) {
reactStream.on('data', data => {
reactDom += decoder.write(data);
});

// inject state into HTML template
reactStream.on('end', () => {
const html = `
<!DOCTYPE html>
Expand All @@ -24,8 +26,13 @@ function htmlTemplate(req, res, next) {
</head>
<body>
<div id="app">${reactDom}</div>
<script src="${ribbitConfig.bundleRoot}.js"></script>
<div id="root">${reactDom}</div>
<script>
window.RIBBIT_PRELOADED_STATE = ${JSON.stringify(preLoadedState).replace(
/</g
)}
</script>
<script src="${ribbitConfig.bundle}"></script>
</body>
</html>
`;
Expand Down
8 changes: 1 addition & 7 deletions server/helpers/buildRoutesCliCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ function buildRoutesCliCommand(command, routes, appParentDirectory, appRoot) {
let homeComponent;
const appRootFile = appRoot ? `/${appRoot}` : '';
routes.forEach(route => {
// if (route.assetName) {
// const routeComponentPair = `${
// route.assetName
// }=${appParentDirectory}${appRootFile}${route.component.slice(1)} `;
// command += routeComponentPair;
// } else {
if (route.route === '/') {
const index = route.component.lastIndexOf('/');
homeComponent = route.component.substring(index, route.component.length - 3);
Expand All @@ -21,8 +15,8 @@ function buildRoutesCliCommand(command, routes, appParentDirectory, appRoot) {
}=${appParentDirectory}${appRootFile}${route.component.slice(1)} `;
command += routeComponentPair;
}
// }
});

return { command, homeComponent };
}

Expand Down
70 changes: 50 additions & 20 deletions server/server.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
// react imports
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';

const React = require('react');

const express = require('express');
const path = require('path');
const fs = require('fs');
const { exec } = require('child_process');
require('isomorphic-fetch');
const bodyParser = require('body-parser');
// const hasPreloadRan = require('../lib/api/hasPreloadRan.js');

const app = express();

// User app directory is received from arguments
const appParentDirectory = process.argv[2];
const ribbitConfig = require(path.join(appParentDirectory, '/ribbit.config.js'));
const ribbitRoutes = require(path.join(
appParentDirectory,
`${ribbitConfig.appRoot}/ribbit.routes.json`
));

const appFile = `${appParentDirectory}/${ribbitConfig.appRoot}/${ribbitConfig.app}`;

// Helper functions imports
const buildRoutesCliCommand = require('./helpers/buildRoutesCliCommand');
const sendFetches = require('./helpers/sendFetches');
Expand Down Expand Up @@ -53,23 +52,50 @@ const routesCliCommand = buildRoutesCliCommand(
ribbitConfig.appRoot
);

const preloadArray = [];

app.use(bodyParser.json());

app.get(['/preload-push', '/preload-pop'], (req, res) => {
const arrayCommand = req.url.substring(req.url.lastIndexOf('-') + 1);
if (arrayCommand === 'push') {
preloadArray.push(1);
res.end();
} else if (arrayCommand === 'pop') {
preloadArray.pop();
res.end();
}
});

app.get(
routeArray,
(req, res, next) => {
const CompiledApp = require(`../dist/App.js`).default;
const context = {};

// user exports their store from wherever they created it
// user must give the path to their store file
const { store } = require(`../dist/App.js`);

const context = { data: {}, head: [], req };
let componentRoute = req.url;

// pull state out of store
const preLoadedState = store.getState();

const jsx = (
<StaticRouter context={context} location={componentRoute}>
<CompiledApp />
</StaticRouter>
// wrap static router in redux Provider in order to user redux state
<Provider store={store}>
<StaticRouter context={context} location={componentRoute}>
<CompiledApp />
</StaticRouter>
</Provider>
);

if (componentRoute === '/') componentRoute = routesCliCommand.homeComponent;

res.locals = {
...res.locals,
preLoadedState,
appParentDirectory,
componentRoute,
jsx,
Expand All @@ -80,18 +106,13 @@ app.get(
htmlTemplate,
writeFile
);
app.use(express.static(ribbitConfig.bundleRoot));

// Create a new child process, that executes the passed in 'cli command'
// Child starts webpack and copies components over to the Ribbit directory
app.use(express.static(ribbitConfig.bundleRoot));

const webpackChild = exec(`${routesCliCommand.command}`, () => {
// start server in callback (after webpack finishes running)
app.listen(4000, () => {
console.log('Listening on port 4000');
// Send fetch request to all routes
const fetchArray = sendFetches(ribbitRoutes, 4000);

app.listen(5000, () => {
console.log('Listening on port 5000');
const fetchArray = sendFetches(ribbitRoutes, 5000);
Promise.all(fetchArray)
.then(arrayOfRoutes => {
const ribbitManifest = arrayOfRoutes.reduce((acc, curr) => {
Expand All @@ -105,10 +126,19 @@ const webpackChild = exec(`${routesCliCommand.command}`, () => {
`${appParentDirectory}/ribbit.manifest.json`,
JSON.stringify(ribbitManifest)
);
unlinkUserDeps(ribbitConfig, appParentDirectory);
process.kill(process.pid, 'SIGINT');

function killServer() {
if (preloadArray.length === 0) {
console.log('KILL THE SERVER GENTS!!!!');
process.kill(process.pid, 'SIGINT');
} else {
console.log('NOT READY TO KILL SERVER');
setTimeout(killServer, 500);
}
}
killServer();
})
.catch();
.catch(err => console.log(err));
});
});

Expand Down

0 comments on commit dd45617

Please sign in to comment.