Skip to content
This repository has been archived by the owner on Oct 9, 2023. It is now read-only.

Separate client and server logic #45

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./lib/client.js');
1 change: 1 addition & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./lib/server.js');
43 changes: 43 additions & 0 deletions src/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import ReactDOM from 'react-dom';
import hypernova, { load } from 'hypernova';

const renderReactClient = (name, component) => {
const payloads = load(name);

if (payloads) {
payloads.forEach((payload) => {
const { node, data } = payload;
const element = React.createElement(component, data);

if (ReactDOM.hydrate) {
ReactDOM.hydrate(element, node);
} else {
ReactDOM.render(element, node);
}
});
}

return component;
};

const renderReact = (name, component) => hypernova({
server() {},

client() {
return renderReactClient(name, component);
},
});

/* istanbul ignore next */
const renderReactStatic = () => hypernova({
server() {},

client() {},
});

export {
renderReactClient,
renderReact,
renderReactStatic,
};
31 changes: 6 additions & 25 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom';
import ReactDOMServer from 'react-dom/server';
import hypernova, { serialize, load } from 'hypernova';
import hypernova from 'hypernova';
import { renderReactClient } from './client';
import { renderReactServer, renderReactStaticServer } from './server';

export const renderReact = (name, component) => hypernova({
server() {
return (props) => {
const contents = ReactDOMServer.renderToString(React.createElement(component, props));
return serialize(name, contents, props);
};
return renderReactServer(name, component);
},

client() {
const payloads = load(name);

if (payloads) {
payloads.forEach((payload) => {
const { node, data } = payload;
const element = React.createElement(component, data);

if (ReactDOM.hydrate) {
ReactDOM.hydrate(element, node);
} else {
ReactDOM.render(element, node);
}
});
}

return component;
return renderReactClient(name, component);
},
});

export const renderReactStatic = (name, component) => hypernova({
server() {
return props => ReactDOMServer.renderToStaticMarkup(React.createElement(component, props));
return renderReactStaticServer(name, component);
},

client() {},
Expand Down
37 changes: 37 additions & 0 deletions src/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import hypernova, { serialize } from 'hypernova';

const renderReactServer = (name, component) => (props) => {
const contents = ReactDOMServer.renderToString(
React.createElement(component, props),
);
return serialize(name, contents, props);
};

const renderReactStaticServer = (name, component) => props =>
// eslint-disable-next-line implicit-arrow-linebreak
ReactDOMServer.renderToStaticMarkup(React.createElement(component, props));

const renderReact = (name, component) => hypernova({
server() {
return renderReactServer(name, component);
},

client() {},
});

const renderReactStatic = (name, component) => hypernova({
server() {
return renderReactStaticServer(name, component);
},

client() {},
});

export {
renderReactServer,
renderReactStaticServer,
renderReact,
renderReactStatic,
};
55 changes: 13 additions & 42 deletions test/renderReact-test.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,48 @@
import jsdom from 'jsdom';
import { assert } from 'chai';
import sinon from 'sinon';
import ReactDOM from 'react-dom';
import ifReact from 'enzyme-adapter-react-helper/build/ifReact';

import ExampleReactComponent from './components/ExampleReactComponent';
import { renderReact } from '..';
import * as renderReactClientModule from '../lib/client';
import * as renderReactServerModule from '../lib/server';

describe('renderReact', () => {
let result;
beforeEach(() => {
result = renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' });
});

it('exists', () => {
assert.isFunction(renderReact);
assert.equal(renderReact.length, 2);
});

it('has correct markup on server', () => {
assert.isString(result);
assert.match(result, /Hello Desmond/);
});

ifReact('>= 16', it, it.skip)('calls hypernova.client (hydrate method)', (done) => {
jsdom.env(result, (err, window) => {
if (err) {
done(err);
return;
}

global.window = window;
global.document = window.document;

const hydrateMethod = sinon.spy(ReactDOM, 'hydrate');

// Calling it again for the client.
renderReact('ExampleReactComponent', ExampleReactComponent);

assert(hydrateMethod.calledOnce);
it('calls renderReactServer', () => {
const renderReactServerSpy = sinon.spy(renderReactServerModule, 'renderReactServer');

delete global.window;
delete global.document;
renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' });

hydrateMethod.restore();
assert(renderReactServerSpy.calledOnce, `renderReactServer was not called once but ${renderReactServerSpy.callCount} times`);

done();
});
renderReactServerSpy.restore();
});

it('calls hypernova.client (render method)', (done) => {
it('calls renderReactClient', (done) => {
const result = renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' });

jsdom.env(result, (err, window) => {
if (err) {
done(err);
return;
}

const sandbox = sinon.createSandbox();
if (ReactDOM.hydrate) {
sandbox.stub(ReactDOM, 'hydrate').value(undefined);
}

const renderMethod = sinon.spy(ReactDOM, 'render');
const renderReactClientSpy = sinon.spy(renderReactClientModule, 'renderReactClient');

global.window = window;
global.document = window.document;

// Calling it again for the client.
renderReact('ExampleReactComponent', ExampleReactComponent);

assert(renderMethod.calledOnce);
assert(renderReactClientSpy.calledOnce, `renderReactClient was not called once but ${renderReactClientSpy.callCount} times`);

sandbox.restore();
renderReactClientSpy.restore();

delete global.window;
delete global.document;
Expand Down
87 changes: 87 additions & 0 deletions test/renderReactClient-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import jsdom from 'jsdom';
import { assert } from 'chai';
import sinon from 'sinon';
import ReactDOM from 'react-dom';
import ifReact from 'enzyme-adapter-react-helper/build/ifReact';

import ExampleReactComponent from './components/ExampleReactComponent';
import { renderReactServer } from '../lib/server';
import { renderReact, renderReactClient } from '../lib/client';

describe('client', () => {
let result;
beforeEach(() => {
result = renderReactServer('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' });

assert.isString(result);
assert.match(result, /Hello Desmond/);
});

it('renderReact exists', () => {
assert.isFunction(renderReact);
assert.equal(renderReact.length, 2);
});

it('renderReactClient exists', () => {
assert.isFunction(renderReactClient);
assert.equal(renderReactClient.length, 2);
});

ifReact('>= 16', it, it.skip)('calls hypernova.client (hydrate method)', (done) => {
jsdom.env(result, (err, window) => {
if (err) {
done(err);
return;
}

global.window = window;
global.document = window.document;

const hydrateMethod = sinon.spy(ReactDOM, 'hydrate');

// Calling it again for the client.
renderReact('ExampleReactComponent', ExampleReactComponent);

assert(hydrateMethod.calledOnce);

hydrateMethod.restore();

delete global.window;
delete global.document;

done();
});
});

it('calls hypernova.client (render method)', (done) => {
jsdom.env(result, (err, window) => {
if (err) {
done(err);
return;
}

const sandbox = sinon.createSandbox();
if (ReactDOM.hydrate) {
sandbox.stub(ReactDOM, 'hydrate').value(undefined);
}

const renderMethod = sinon.spy(ReactDOM, 'render');

global.window = window;
global.document = window.document;

// Calling it again for the client.
renderReact('ExampleReactComponent', ExampleReactComponent);

assert(renderMethod.calledOnce);

sandbox.restore();
renderMethod.restore();

delete global.window;
delete global.document;

done();
});
});
});
26 changes: 26 additions & 0 deletions test/renderReactServer-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { assert } from 'chai';

import ExampleReactComponent from './components/ExampleReactComponent';
import { renderReact, renderReactServer } from '../lib/server';

describe('server renderReact', () => {
let result;
beforeEach(() => {
result = renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' });
});

it('exists', () => {
assert.isFunction(renderReact);
assert.equal(renderReact.length, 2);
});

it('exists', () => {
assert.isFunction(renderReactServer);
assert.equal(renderReactServer.length, 2);
});

it('has correct markup', () => {
assert.isString(result);
assert.match(result, /Hello Desmond/);
});
});
18 changes: 10 additions & 8 deletions test/renderReactStatic-test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { assert } from 'chai';
import sinon from 'sinon';

import ExampleReactComponent from './components/ExampleReactComponent';
import { renderReactStatic } from '..';
import * as renderReactServerModule from '../lib/server';

describe('renderReactStatic', () => {
let result;
beforeEach(() => {
result = renderReactStatic('ExampleReactComponent', ExampleReactComponent)({ name: 'Zack' });
});

it('exists', () => {
assert.isFunction(renderReactStatic);
assert.equal(renderReactStatic.length, 2);
});

it('has correct markup on server', () => {
assert.isString(result);
assert.match(result, /Hello Zack/);
it('calls renderReactStaticServer', () => {
const renderReactStaticServerSpy = sinon.spy(renderReactServerModule, 'renderReactStaticServer');

renderReactStatic('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' });

assert(renderReactStaticServerSpy.calledOnce, `renderReactServer was not called once but ${renderReactStaticServerSpy.callCount} times`);

renderReactStaticServerSpy.restore();
});
});
18 changes: 18 additions & 0 deletions test/renderReactStaticServer-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { assert } from 'chai';

import ExampleReactComponent from './components/ExampleReactComponent';
import { renderReactStatic } from '../lib/server';

describe('server renderReactStatic', () => {
it('exists', () => {
assert.isFunction(renderReactStatic);
assert.equal(renderReactStatic.length, 2);
});

it('has correct markup', () => {
const result = renderReactStatic('ExampleReactComponent', ExampleReactComponent)({ name: 'Zack' });

assert.isString(result);
assert.match(result, /Hello Zack/);
});
});