Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support hooks #980

Merged
merged 5 commits into from
Mar 27, 2019
Merged
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
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