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];