diff --git a/packages/rax-server-renderer/package.json b/packages/rax-server-renderer/package.json
index 5adabc281a..a86fc4433e 100644
--- a/packages/rax-server-renderer/package.json
+++ b/packages/rax-server-renderer/package.json
@@ -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",
@@ -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"
}
}
diff --git a/packages/rax-server-renderer/src/__tests__/renderToString.js b/packages/rax-server-renderer/src/__tests__/renderToString.js
index 262c81f550..dbb8400df8 100644
--- a/packages/rax-server-renderer/src/__tests__/renderToString.js
+++ b/packages/rax-server-renderer/src/__tests__/renderToString.js
@@ -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', () => {
@@ -91,4 +91,78 @@ describe('renderToString', () => {
let str = renderToString();
expect(str).toBe('');
});
+
+ it('render with state hook', () => {
+ function MyComponent(props) {
+ const [name, setName] = useState(props.name);
+
+ return (
+
Hello {name}
+ );
+ };
+
+ let str = renderToString();
+ expect(str).toBe(' Hello rax
');
+ });
+
+ it('render with effect hook', () => {
+ function MyComponent(props) {
+ const [name, setName] = useState(props.name);
+
+ useEffect(() => {
+ // ...
+ });
+
+ return (
+ Hello {name}
+ );
+ };
+
+ let str = renderToString();
+ expect(str).toBe(' Hello rax
');
+ });
+
+ it('render with context hook', () => {
+ const NumberContext = createContext(5);
+
+ function MyComponent() {
+ const value = useContext(NumberContext);
+
+ return (
+ The answer is {value}.
+ );
+ };
+
+ let str = renderToString();
+ expect(str).toBe('The answer is 5.
');
+ });
+
+ 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 (
+ Count: {state.count}
+ );
+ }
+
+ let str = renderToString();
+ expect(str).toBe('Count: 0
');
+ });
});
diff --git a/packages/rax-server-renderer/src/index.js b/packages/rax-server-renderer/src/index.js
index cb769bf0dc..4d2039a08e 100644
--- a/packages/rax-server-renderer/src/index.js
+++ b/packages/rax-server-renderer/src/index.js
@@ -1,3 +1,5 @@
+import { shared } from 'rax';
+
const EMPTY_OBJECT = {};
const TRUE = true;
const UNITLESS_NUMBER_PROPS = {
@@ -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);
@@ -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;
@@ -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];