Skip to content

Commit

Permalink
Fixed error when hydrating forwardRef component Github #1486
Browse files Browse the repository at this point in the history
  • Loading branch information
Havunen committed Aug 22, 2019
1 parent 2c1b935 commit 646753c
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 13 deletions.
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,26 +83,26 @@
"@babel/plugin-proposal-class-properties": "7.5.5",
"@babel/preset-env": "7.5.5",
"@babel/preset-typescript": "^7.3.3",
"@types/history": "^4.7.2",
"@types/jest": "^24.0.17",
"@types/node": "^12.7.1",
"@types/history": "^4.7.3",
"@types/jest": "^24.0.18",
"@types/node": "^12.7.2",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^24.8.0",
"babel-jest": "^24.9.0",
"babel-plugin-inferno": "6.1.0",
"cli-table": "^0.3.1",
"colors": "^1.3.3",
"concat-stream-es6": "0.0.1",
"coveralls": "^3.0.6",
"cross-env": "^5.2.0",
"d3-scale-chromatic": "^1.4.0",
"d3-scale-chromatic": "^1.5.0",
"filesize": "^4.1.2",
"gzip-size": "^5.1.1",
"history": "^4.9.0",
"jest": "^24.8.0",
"jest": "^24.9.0",
"jest-silent-reporter": "^0.1.2",
"jsdom": "15.1.1",
"lerna": "3.16.4",
"lint-staged": "^9.2.1",
"lint-staged": "^9.2.3",
"madge": "^3.4.4",
"merge-dirs": "^0.2.1",
"minimist": "^1.2.0",
Expand All @@ -111,8 +111,8 @@
"perf-monitor": "^0.4.1",
"pre-commit": "^1.2.2",
"prettier": "^1.18.2",
"rimraf": "^2.7.0",
"rollup": "^1.19.4",
"rimraf": "^3.0.0",
"rollup": "^1.20.0",
"rollup-plugin-alias": "^1.5.2",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-buble": "^0.19.8",
Expand All @@ -122,7 +122,7 @@
"rollup-plugin-terser": "^5.1.1",
"rollup-plugin-typescript2": "0.22.1",
"sinon": "^7.4.1",
"tslint": "^5.18.0",
"tslint": "^5.19.0",
"tslint-config-prettier": "^1.18.0",
"typescript": "3.5.3"
}
Expand Down
133 changes: 133 additions & 0 deletions packages/inferno-hydrate/__tests__/hydrate-forward-ref.spec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Component, createRef, forwardRef, render } from 'inferno';
import { hydrate } from 'inferno-hydrate';

describe('Hydrate - Forward Ref', () => {
let container;

beforeEach(function() {
container = document.createElement('div');
document.body.appendChild(container);
});

afterEach(function() {
render(null, container);
container.innerHTML = '';
document.body.removeChild(container);
});

it('Should be possible to forward createRef', () => {
const FancyButton = forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));

expect(FancyButton.render).toBeDefined();

class Hello extends Component {
constructor(props) {
super(props);

// You can now get a ref directly to the DOM button:
this.btn = createRef();
}

componentDidMount() {
expect(this.btn.current).toBe(container.querySelector('button'));
}
render() {
return <FancyButton ref={this.btn}>Click me!</FancyButton>;
}
}

container.innerHTML = '<button class="FancyButton">Click me!</button>';

hydrate(<Hello />, container);

expect(container.innerHTML).toBe('<button class="FancyButton">Click me!</button>');
});

it('Should be possible to forward callback ref', () => {
const FancyButton = forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));

expect(FancyButton.render).toBeDefined();

class Hello extends Component {
render() {
return (
<FancyButton
ref={btn => {
if (btn) {
expect(btn).toBe(container.querySelector('button'));
}
}}
>
Click me!
</FancyButton>
);
}
}

container.innerHTML = '<button class="FancyButton">Click me!</button>';

hydrate(<Hello />, container);

expect(container.innerHTML).toBe('<button class="FancyButton">Click me!</button>');

render(null, container);

expect(container.innerHTML).toBe('');
});

it('Should be possible to patch forwardRef component', () => {
const FancyButton = forwardRef((props, ref) => {
return (
<button ref={ref} className="FancyButton">
{props.children}
</button>
);
});

expect(FancyButton.render).toBeDefined();

let firstVal = null;

container.innerHTML = '<button class="FancyButton">Click me!</button>';

hydrate(
<FancyButton
ref={btn => {
firstVal = btn;
}}
>
Click me!
</FancyButton>,
container
);

expect(container.innerHTML).toBe('<button class="FancyButton">Click me!</button>');
expect(firstVal).not.toBe(null);

let secondVal = null;

render(
<FancyButton
ref={btn => {
secondVal = btn;
}}
>
Click me! 222
</FancyButton>,
container
);

expect(firstVal).toBe(null);
expect(secondVal).not.toBe(null);

expect(container.innerHTML).toBe('<button class="FancyButton">Click me! 222</button>');
});
});
10 changes: 7 additions & 3 deletions packages/inferno-hydrate/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {isFunction, isInvalid, isNull, isNullOrUndef, throwError, warning} from 'inferno-shared';
import {ChildFlags, VNodeFlags} from 'inferno-vnode-flags';
import {_CI, _HI, _M, _MCCC, _ME, _MFCC, _MP, _MR, render, VNode} from 'inferno';
import {_CI, _HI, _M, _MCCC, _ME, _MFCC, _MP, _MR, EMPTY_OBJ, render, VNode} from 'inferno';

function isSameInnerHTML(dom: Element, innerHTML: string): boolean {
const tempdom = document.createElement('i');
Expand Down Expand Up @@ -38,10 +38,14 @@ function isSamePropsInnerHTML(dom: Element, props): boolean {
return Boolean(props && props.dangerouslySetInnerHTML && props.dangerouslySetInnerHTML.__html && isSameInnerHTML(dom, props.dangerouslySetInnerHTML.__html));
}

function renderFunctionalComponent(vNode: VNode, context, props) {
return vNode.flags & VNodeFlags.ForwardRef ? vNode.type.render(props, vNode.ref, context) : vNode.type(props, context);
}

function hydrateComponent(vNode: VNode, parentDOM: Element, dom: Element, context, isSVG: boolean, isClass: boolean, lifecycle: Function[]) {
const type = vNode.type as Function;
const ref = vNode.ref;
const props = vNode.props || {};
const props = vNode.props || EMPTY_OBJ;
let currentNode;

if (isClass) {
Expand All @@ -51,7 +55,7 @@ function hydrateComponent(vNode: VNode, parentDOM: Element, dom: Element, contex
currentNode = hydrateVNode(input, parentDOM, dom, instance.$CX, isSVG, lifecycle);
_MCCC(ref, instance, lifecycle);
} else {
const input = _HI(type(props, context));
const input = _HI(renderFunctionalComponent(vNode, context, props));
currentNode = hydrateVNode(input, parentDOM, dom, context, isSVG, lifecycle);
vNode.children = input;
_MFCC(vNode, lifecycle);
Expand Down

0 comments on commit 646753c

Please sign in to comment.