Skip to content

Commit

Permalink
Merge pull request #980 from alibaba/feat/rax-server-renderer
Browse files Browse the repository at this point in the history
feat: support hooks
  • Loading branch information
yuanyan authored Mar 27, 2019
2 parents f306402 + 6b72dde commit fe26846
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 6 deletions.
6 changes: 3 additions & 3 deletions packages/rax-server-renderer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rax-server-renderer",
"version": "0.6.5",
"version": "1.0.0",
"description": "Rax renderer for server-side render.",
"license": "BSD-3-Clause",
"main": "lib/index.js",
Expand All @@ -12,7 +12,7 @@
"url": "https://github.com/alibaba/rax/issues"
},
"homepage": "https://github.com/alibaba/rax#readme",
"devDependencies": {
"rax": "^0.6.5"
"peerDependencies": {
"rax": "^1.0.0"
}
}
76 changes: 75 additions & 1 deletion packages/rax-server-renderer/src/__tests__/renderToString.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @jsx createElement */

import {createElement} from 'rax';
import {createElement, useState, useEffect, createContext, useContext, useReducer} from 'rax';
import {renderToString} from '../index';

describe('renderToString', () => {
Expand Down Expand Up @@ -91,4 +91,78 @@ describe('renderToString', () => {
let str = renderToString(<MyComponent />);
expect(str).toBe('<input type="radio" checked="checked">');
});

it('render with state hook', () => {
function MyComponent(props) {
const [name, setName] = useState(props.name);

return (
<h1> Hello {name}</h1>
);
};

let str = renderToString(<MyComponent name="rax" />);
expect(str).toBe('<h1> Hello rax</h1>');
});

it('render with effect hook', () => {
function MyComponent(props) {
const [name, setName] = useState(props.name);

useEffect(() => {
// ...
});

return (
<h1> Hello {name}</h1>
);
};

let str = renderToString(<MyComponent name="rax" />);
expect(str).toBe('<h1> Hello rax</h1>');
});

it('render with context hook', () => {
const NumberContext = createContext(5);

function MyComponent() {
const value = useContext(NumberContext);

return (
<div>The answer is {value}.</div>
);
};

let str = renderToString(<MyComponent />);
expect(str).toBe('<div>The answer is 5.</div>');
});

it('render with reducer hook', () => {
const initialState = {count: 0};

function reducer(state, action) {
switch (action.type) {
case 'reset':
return initialState;
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
// A reducer must always return a valid state.
// Alternatively you can throw an error if an invalid action is dispatched.
return state;
}
}

function MyComponent({initialCount}) {
const [state] = useReducer(reducer, {count: initialCount});
return (
<div>Count: {state.count}</div>
);
}

let str = renderToString(<MyComponent initialCount={0} />);
expect(str).toBe('<div>Count: 0</div>');
});
});
51 changes: 49 additions & 2 deletions packages/rax-server-renderer/src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { shared } from 'rax';

const EMPTY_OBJECT = {};
const TRUE = true;
const UNITLESS_NUMBER_PROPS = {
Expand Down Expand Up @@ -126,6 +128,44 @@ const updater = {
}
};

/**
* Functional Reactive Component Class Wrapper
*/
class ReactiveComponent {
constructor(pureRender) {
// A pure function
this._render = pureRender;
this._hookID = 0;
this._hooks = {};
// Handles store
this.didMount = [];
this.didUpdate = [];
this.willUnmount = [];
}

getHooks() {
return this._hooks;
}

getHookID() {
return ++this._hookID;
}

readContext(context) {
const Provider = context.Provider;

return Provider.defaultValue;
}

render() {
this._hookID = 0;

let children = this._render(this.props, this.context);

return children;
}
}

function renderElementToString(element, context, options) {
if (typeof element === 'string') {
return escapeText(element);
Expand All @@ -146,7 +186,6 @@ function renderElementToString(element, context, options) {

if (type) {
const props = element.props || EMPTY_OBJECT;

if (type.prototype && type.prototype.render) {
const instance = new type(props, context, updater); // eslint-disable-line new-cap
let currentContext = instance.context = context;
Expand Down Expand Up @@ -182,7 +221,15 @@ function renderElementToString(element, context, options) {
var renderedElement = instance.render();
return renderElementToString(renderedElement, currentContext, options);
} else if (typeof type === 'function') {
var renderedElement = type(props, context);
const instance = new ReactiveComponent(type);
instance.props = props;
instance.context = context;

shared.Host.owner = {
_instance: instance
};

const renderedElement = instance.render();
return renderElementToString(renderedElement, context, options);
} else if (typeof type === 'string') {
const isVoidElement = VOID_ELEMENTS[type];
Expand Down

0 comments on commit fe26846

Please sign in to comment.