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

[Fiber][WIP] Shallow Renderer #8808

Closed
wants to merge 7 commits into from
Closed
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
5 changes: 5 additions & 0 deletions scripts/fiber/tests-failing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,8 @@ src/renderers/shared/shared/__tests__/ReactUpdates-test.js

src/renderers/shared/shared/__tests__/refs-test.js
* Should increase refs with an increase in divs

src/test/__tests__/ReactTestUtils-test.js
* can access the mounted component instance
* can setState in componentWillMount when shallow rendering
* can pass context when shallowly rendering
3 changes: 0 additions & 3 deletions scripts/fiber/tests-passing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1736,11 +1736,8 @@ src/test/__tests__/ReactTestUtils-test.js
* can shallow render to null
* can shallow render with a ref
* lets you update shallowly rendered components
* can access the mounted component instance
* can shallowly render components with contextTypes
* can shallowly render components with ref as function
* can setState in componentWillMount when shallow rendering
* can pass context when shallowly rendering
* can fail context when shallowly rendering
* can scryRenderedDOMComponentsWithClass with TextComponent
* can scryRenderedDOMComponentsWithClass with className contains \n
Expand Down
137 changes: 5 additions & 132 deletions src/test/ReactShallowRenderer.js
Original file line number Diff line number Diff line change
@@ -1,145 +1,18 @@
/**
* Copyright 2013-present, Facebook, Inc.
* Copyright (c) 2013-present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactShallowRenderer
* @preventMunge
*/

'use strict';

var React = require('React');
var ReactDOMInjection = require('ReactDOMInjection');
var ReactDOMStackInjection = require('ReactDOMStackInjection');
var ReactCompositeComponent = require('ReactCompositeComponent');
var ReactReconciler = require('ReactReconciler');
var ReactUpdates = require('ReactUpdates');
const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');

var emptyObject = require('emptyObject');
var getNextDebugID = require('getNextDebugID');
var invariant = require('invariant');

class NoopInternalComponent {
constructor(element) {
this._renderedOutput = element;
this._currentElement = element;

if (__DEV__) {
this._debugID = getNextDebugID();
}
}
mountComponent() {}
receiveComponent(element) {
this._renderedOutput = element;
this._currentElement = element;
}
unmountComponent() {}
getHostNode() {
return undefined;
}
getPublicInstance() {
return null;
}
}

var ShallowComponentWrapper = function(element) {
// TODO: Consolidate with instantiateReactComponent
if (__DEV__) {
this._debugID = getNextDebugID();
}

this.construct(element);
};
Object.assign(
ShallowComponentWrapper.prototype,
ReactCompositeComponent, {
_constructComponent:
ReactCompositeComponent._constructComponentWithoutOwner,
_instantiateReactComponent: function(element) {
return new NoopInternalComponent(element);
},
_replaceNodeWithMarkup: function() {},
_renderValidatedComponent:
ReactCompositeComponent
._renderValidatedComponentWithoutOwnerOrContext,
}
);

function _batchedRender(renderer, element, context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true);
renderer._render(element, transaction, context);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}

class ReactShallowRenderer {
_instance = null;
getMountedInstance() {
return this._instance ? this._instance._instance : null;
}
render(element, context) {
// Ensure we've done the default injections. This might not be true in the
// case of a simple test that only requires React and the TestUtils in
// conjunction with an inline-requires transform.
ReactDOMInjection.inject();
ReactDOMStackInjection.inject();

invariant(
React.isValidElement(element),
'ReactShallowRenderer render(): Invalid component element.%s',
typeof element === 'function' ?
' Instead of passing a component class, make sure to instantiate ' +
'it by passing it to React.createElement.' :
''
);
invariant(
typeof element.type !== 'string',
'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
'components, not primitives (%s). Instead of calling `.render(el)` and ' +
'inspecting the rendered output, look at `el.props` directly instead.',
element.type
);

if (!context) {
context = emptyObject;
}
ReactUpdates.batchedUpdates(_batchedRender, this, element, context);

return this.getRenderOutput();
}
getRenderOutput() {
return (
(this._instance && this._instance._renderedComponent &&
this._instance._renderedComponent._renderedOutput)
|| null
);
}
unmount() {
if (this._instance) {
ReactReconciler.unmountComponent(
this._instance,
false, /* safely */
false /* skipLifecycle */
);
}
}
_render(element, transaction, context) {
if (this._instance) {
ReactReconciler.receiveComponent(
this._instance,
element,
transaction,
context
);
} else {
var instance = new ShallowComponentWrapper(element);
ReactReconciler.mountComponent(instance, transaction, null, null, context, 0);
this._instance = instance;
}
}
}

module.exports = ReactShallowRenderer;
module.exports = ReactDOMFeatureFlags.useFiber
? require('ReactShallowRendererFiber')
: require('ReactShallowRendererStack');
165 changes: 165 additions & 0 deletions src/test/ReactShallowRendererFiber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactShallowRendererFiber
* @flow
*/

'use strict';

const React = require('React');
const emptyObject = require('emptyObject');
const ReactFiberReconciler = require('ReactFiberReconciler');
const ReactElementSymbol = require('ReactElementSymbol');
const invariant = require('invariant');

type Props = {
children : Array<any>,
};

type Container = {
element : Instance | TextInstance | {},
context : Object,
};

type Instance = {|
type : string,
$$typeof : number,
props : Object,
ref : null,
key : null,
_store : Object,
_owner : null,
|};

type TextInstance = {|
text : string,
|};

const reconciler = ReactFiberReconciler({
getRootHostContext(rootContainerInstance : Container) {
return rootContainerInstance.context || emptyObject;
},

getChildHostContext(parentHostContext, type) {
return parentHostContext;
},

getPublicInstance(instance) {
return instance;
},

createInstance(type : string, props : Props, hostContext, internalInstanceHandle) : Instance {
const { children, ...instanceProps } = props;

if (children) {
instanceProps.children = [];
}

return {
type,
$$typeof: ReactElementSymbol,
props: instanceProps,
key: null,
_owner: null,
_store: {},
ref: null,
};
},

appendInitialChild(parentInstance, child) {
parentInstance.props.children.push(child);
},
finalizeInitialChildren(parentInstance, type, props, rootContainerInstance) : boolean {
return false;
},

prepareUpdate(instance, type, oldProps, newProps, hostContext) : boolean {
return true;
},
commitUpdate(instance, type, oldProps, newProps, rootContainerInstance, internalInstanceHandle) {
instance.props = newProps;
},
commitMount(instance, type, newProps, rootContainerInstance, internalInstanceHandle) {},

shouldSetTextContent(props : Props) : boolean {
return (
typeof props.children === 'string' ||
typeof props.children === 'number'
);
},
resetTextContent(instance) {},

createTextInstance(text : string) : TextInstance {
return { text };
},
commitTextUpdate(textInstance, oldText, newText) {},

appendChild(parentInstance : Container | Instance, child : Instance | TextInstance) {
if (parentInstance.element) {
parentInstance.element = child;
}
},
insertBefore(parentInstance, child, beforeChild) {},
removeChild(parentInstance, child) {},

prepareForCommit() {},
resetAfterCommit(commitInfo) {},

scheduleAnimationCallback: window.requestAnimaionFrame,
scheduleDeferredCallback: window.requestIdleCallback,
useSyncScheduling: true,
});

class ReactShallowRendererFiber {
root_ = (null : ?Object)

render(element : React$Element<*>, context = emptyObject) {
invariant(
React.isValidElement(element),
'ReactShallowRenderer render(): Invalid component element.%s',
typeof element === 'function' ?
' Instead of passing a component class, make sure to instantiate ' +
'it by passing it to React.createElement.' :
''
);
invariant(
typeof element.type !== 'string',
'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
'components, not primitives (%s). Instead of calling `.render(el)` and ' +
'inspecting the rendered output, look at `el.props` directly instead.',
element.type
);

if (this.root_ == null) {
this.root_ = reconciler.createContainer({
context,
element: emptyObject,
});
}

reconciler.updateContainer(element, this.root_, null, null);
return this.getRenderOutput();
}

unmount() {
if (this.root_ != null) {
reconciler.updateContainer(null, this.root_, null, () => {
this.root_ = null;
});
}
}

getRenderOutput() {
return this.root_ != null
? reconciler.findHostInstance(this.root_)
: null;
}
}

module.exports = ReactShallowRendererFiber;
Loading