mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
This reverts commit a878c3056d.
The previous stuff that this builds on didn't successfully land fully. Since I
want to measure this in isolation, I'll revert this for now.
This commit is contained in:
committed by
GitHub
parent
d885894c6e
commit
b2eaafaf3b
@@ -147,6 +147,8 @@ src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
|
||||
* should validate against invalid styles
|
||||
* should track input values
|
||||
* should track textarea values
|
||||
* should execute custom event plugin listening behavior
|
||||
* should handle null and missing properly with event hooks
|
||||
* should warn for children on void elements
|
||||
* should support custom elements which extend native elements
|
||||
* should warn against children for void elements
|
||||
@@ -155,6 +157,7 @@ src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
|
||||
* should warn about contentEditable and children
|
||||
* should validate against invalid styles
|
||||
* should report component containing invalid styles
|
||||
* should clean up listeners
|
||||
* should clean up input value tracking
|
||||
* should clean up input textarea tracking
|
||||
* should warn about the `onScroll` issue when unsupported (IE8)
|
||||
@@ -379,11 +382,13 @@ src/renderers/dom/stack/server/__tests__/ReactServerRendering-test.js
|
||||
* should generate simple markup for self-closing tags
|
||||
* should generate simple markup for attribute with `>` symbol
|
||||
* should generate comment markup for component returns null
|
||||
* should not register event listeners
|
||||
* should render composite components
|
||||
* should only execute certain lifecycle methods
|
||||
* should have the correct mounting behavior
|
||||
* should not put checksum and React ID on components
|
||||
* should not put checksum and React ID on text components
|
||||
* should not register event listeners
|
||||
* should only execute certain lifecycle methods
|
||||
* allows setState in componentWillMount without using DOM
|
||||
* renders components with different batching strategies
|
||||
@@ -498,9 +503,6 @@ src/renderers/shared/shared/__tests__/ReactTreeTraversal-test.js
|
||||
* should leave to the window
|
||||
* should leave to the window from the shallowest
|
||||
|
||||
src/renderers/shared/shared/event/__tests__/EventPluginHub-test.js
|
||||
* should prevent non-function listeners, at dispatch
|
||||
|
||||
src/renderers/shared/stack/reconciler/__tests__/ReactChildReconciler-test.js
|
||||
* warns for duplicated keys
|
||||
* warns for duplicated keys with component stack info
|
||||
|
||||
@@ -955,6 +955,9 @@ src/renderers/shared/shared/__tests__/ReactTreeTraversal-test.js
|
||||
* should not traverse if enter/leave the same node
|
||||
* should determine the first common ancestor correctly
|
||||
|
||||
src/renderers/shared/shared/event/__tests__/EventPluginHub-test.js
|
||||
* should prevent non-function listeners
|
||||
|
||||
src/renderers/shared/shared/event/__tests__/EventPluginRegistry-test.js
|
||||
* should be able to inject ordering before plugins
|
||||
* should be able to inject plugins before and after ordering
|
||||
@@ -1059,6 +1062,7 @@ src/renderers/shared/stack/reconciler/__tests__/ReactErrorBoundaries-test.js
|
||||
* propagates errors on retry on mounting
|
||||
* propagates errors inside boundary during componentWillMount
|
||||
* propagates errors inside boundary while rendering error state
|
||||
* does not register event handlers for unmounted children
|
||||
* does not call componentWillUnmount when aborting initial mount
|
||||
* resets refs if mounting aborts
|
||||
* successfully mounts if no error occurs
|
||||
|
||||
@@ -163,6 +163,16 @@ function getListeningForDocument(mountAt) {
|
||||
return alreadyListeningTo[mountAt[topListenersIDKey]];
|
||||
}
|
||||
|
||||
/**
|
||||
* `ReactBrowserEventEmitter` is used to attach top-level event listeners. For
|
||||
* example:
|
||||
*
|
||||
* EventPluginHub.putListener('myID', 'onClick', myFunction);
|
||||
*
|
||||
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,9 +15,8 @@ var EventListener;
|
||||
var EventPluginHub;
|
||||
var EventPluginRegistry;
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMComponentTree;
|
||||
var ReactBrowserEventEmitter;
|
||||
var ReactDOMComponentTree;
|
||||
var ReactTestUtils;
|
||||
var TapEventPlugin;
|
||||
|
||||
@@ -44,17 +43,18 @@ var GRANDPARENT;
|
||||
var PARENT;
|
||||
var CHILD;
|
||||
|
||||
var getListener;
|
||||
var putListener;
|
||||
var deleteAllListeners;
|
||||
|
||||
function registerSimpleTestHandler() {
|
||||
putListener(CHILD, ON_CLICK_KEY, LISTENER);
|
||||
var listener = getListener(CHILD, ON_CLICK_KEY);
|
||||
EventPluginHub.putListener(getInternal(CHILD), ON_CLICK_KEY, LISTENER);
|
||||
var listener = EventPluginHub.getListener(getInternal(CHILD), ON_CLICK_KEY);
|
||||
expect(listener).toEqual(LISTENER);
|
||||
return getListener(CHILD, ON_CLICK_KEY);
|
||||
return EventPluginHub.getListener(getInternal(CHILD), ON_CLICK_KEY);
|
||||
}
|
||||
|
||||
function getInternal(node) {
|
||||
return ReactDOMComponentTree.getInstanceFromNode(node);
|
||||
}
|
||||
|
||||
|
||||
describe('ReactBrowserEventEmitter', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModuleRegistry();
|
||||
@@ -63,67 +63,18 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
EventPluginHub = require('EventPluginHub');
|
||||
EventPluginRegistry = require('EventPluginRegistry');
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
TapEventPlugin = require('TapEventPlugin');
|
||||
|
||||
var container = document.createElement('div');
|
||||
|
||||
var GRANDPARENT_PROPS = {};
|
||||
var PARENT_PROPS = {};
|
||||
var CHILD_PROPS = {};
|
||||
|
||||
function renderTree() {
|
||||
ReactDOM.render(
|
||||
<div ref={(c) => GRANDPARENT = c} {...GRANDPARENT_PROPS}>
|
||||
<div ref={(c) => PARENT = c} {...PARENT_PROPS}>
|
||||
<div ref={(c) => CHILD = c} {...CHILD_PROPS} />
|
||||
</div>
|
||||
</div>,
|
||||
container
|
||||
);
|
||||
}
|
||||
|
||||
renderTree();
|
||||
|
||||
getListener = function(node, eventName) {
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node);
|
||||
return EventPluginHub.getListener(
|
||||
inst,
|
||||
eventName
|
||||
);
|
||||
};
|
||||
putListener = function(node, eventName, listener) {
|
||||
switch (node) {
|
||||
case CHILD:
|
||||
CHILD_PROPS[eventName] = listener;
|
||||
break;
|
||||
case PARENT:
|
||||
PARENT_PROPS[eventName] = listener;
|
||||
break;
|
||||
case GRANDPARENT:
|
||||
GRANDPARENT_PROPS[eventName] = listener;
|
||||
break;
|
||||
}
|
||||
// Rerender with new event listeners
|
||||
renderTree();
|
||||
};
|
||||
deleteAllListeners = function(node) {
|
||||
switch (node) {
|
||||
case CHILD:
|
||||
CHILD_PROPS = {};
|
||||
break;
|
||||
case PARENT:
|
||||
PARENT_PROPS = {};
|
||||
break;
|
||||
case GRANDPARENT:
|
||||
GRANDPARENT_PROPS = {};
|
||||
break;
|
||||
}
|
||||
renderTree();
|
||||
};
|
||||
ReactTestUtils.renderIntoDocument(
|
||||
<div ref={(c) => GRANDPARENT = c}>
|
||||
<div ref={(c) => PARENT = c}>
|
||||
<div ref={(c) => CHILD = c} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
idCallOrder = [];
|
||||
tapMoveThreshold = TapEventPlugin.tapMoveThreshold;
|
||||
@@ -134,20 +85,20 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
|
||||
it('should store a listener correctly', () => {
|
||||
registerSimpleTestHandler();
|
||||
var listener = getListener(CHILD, ON_CLICK_KEY);
|
||||
var listener = EventPluginHub.getListener(getInternal(CHILD), ON_CLICK_KEY);
|
||||
expect(listener).toBe(LISTENER);
|
||||
});
|
||||
|
||||
it('should retrieve a listener correctly', () => {
|
||||
registerSimpleTestHandler();
|
||||
var listener = getListener(CHILD, ON_CLICK_KEY);
|
||||
var listener = EventPluginHub.getListener(getInternal(CHILD), ON_CLICK_KEY);
|
||||
expect(listener).toEqual(LISTENER);
|
||||
});
|
||||
|
||||
it('should clear all handlers when asked to', () => {
|
||||
registerSimpleTestHandler();
|
||||
deleteAllListeners(CHILD);
|
||||
var listener = getListener(CHILD, ON_CLICK_KEY);
|
||||
EventPluginHub.deleteAllListeners(getInternal(CHILD));
|
||||
var listener = EventPluginHub.getListener(getInternal(CHILD), ON_CLICK_KEY);
|
||||
expect(listener).toBe(undefined);
|
||||
});
|
||||
|
||||
@@ -171,153 +122,153 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
);
|
||||
|
||||
it('should bubble simply', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, CHILD)
|
||||
recordID.bind(null, getInternal(CHILD))
|
||||
);
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, PARENT)
|
||||
recordID.bind(null, getInternal(PARENT))
|
||||
);
|
||||
putListener(
|
||||
GRANDPARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(GRANDPARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, GRANDPARENT)
|
||||
recordID.bind(null, getInternal(GRANDPARENT))
|
||||
);
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
expect(idCallOrder[2]).toBe(GRANDPARENT);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
expect(idCallOrder[1]).toBe(getInternal(PARENT));
|
||||
expect(idCallOrder[2]).toBe(getInternal(GRANDPARENT));
|
||||
});
|
||||
|
||||
it('should continue bubbling if an error is thrown', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, CHILD)
|
||||
recordID.bind(null, getInternal(CHILD))
|
||||
);
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_CLICK_KEY,
|
||||
function() {
|
||||
recordID(PARENT);
|
||||
recordID(getInternal(PARENT));
|
||||
throw new Error('Handler interrupted');
|
||||
}
|
||||
);
|
||||
putListener(
|
||||
GRANDPARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(GRANDPARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, GRANDPARENT)
|
||||
recordID.bind(null, getInternal(GRANDPARENT))
|
||||
);
|
||||
expect(function() {
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
}).toThrow();
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
expect(idCallOrder[2]).toBe(GRANDPARENT);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
expect(idCallOrder[1]).toBe(getInternal(PARENT));
|
||||
expect(idCallOrder[2]).toBe(getInternal(GRANDPARENT));
|
||||
});
|
||||
|
||||
it('should set currentTarget', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_CLICK_KEY,
|
||||
function(event) {
|
||||
recordID(CHILD);
|
||||
recordID(getInternal(CHILD));
|
||||
expect(event.currentTarget).toBe(CHILD);
|
||||
}
|
||||
);
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_CLICK_KEY,
|
||||
function(event) {
|
||||
recordID(PARENT);
|
||||
recordID(getInternal(PARENT));
|
||||
expect(event.currentTarget).toBe(PARENT);
|
||||
}
|
||||
);
|
||||
putListener(
|
||||
GRANDPARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(GRANDPARENT),
|
||||
ON_CLICK_KEY,
|
||||
function(event) {
|
||||
recordID(GRANDPARENT);
|
||||
recordID(getInternal(GRANDPARENT));
|
||||
expect(event.currentTarget).toBe(GRANDPARENT);
|
||||
}
|
||||
);
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
expect(idCallOrder[2]).toBe(GRANDPARENT);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
expect(idCallOrder[1]).toBe(getInternal(PARENT));
|
||||
expect(idCallOrder[2]).toBe(getInternal(GRANDPARENT));
|
||||
});
|
||||
|
||||
it('should support stopPropagation()', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, CHILD)
|
||||
recordID.bind(null, getInternal(CHILD))
|
||||
);
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordIDAndStopPropagation.bind(null, PARENT)
|
||||
recordIDAndStopPropagation.bind(null, getInternal(PARENT))
|
||||
);
|
||||
putListener(
|
||||
GRANDPARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(GRANDPARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, GRANDPARENT)
|
||||
recordID.bind(null, getInternal(GRANDPARENT))
|
||||
);
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
expect(idCallOrder.length).toBe(2);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
expect(idCallOrder[1]).toBe(getInternal(PARENT));
|
||||
});
|
||||
|
||||
it('should stop after first dispatch if stopPropagation', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_CLICK_KEY,
|
||||
recordIDAndStopPropagation.bind(null, CHILD)
|
||||
recordIDAndStopPropagation.bind(null, getInternal(CHILD))
|
||||
);
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, PARENT)
|
||||
recordID.bind(null, getInternal(PARENT))
|
||||
);
|
||||
putListener(
|
||||
GRANDPARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(GRANDPARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, GRANDPARENT)
|
||||
recordID.bind(null, getInternal(GRANDPARENT))
|
||||
);
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
expect(idCallOrder.length).toBe(1);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
});
|
||||
|
||||
it('should not stopPropagation if false is returned', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_CLICK_KEY,
|
||||
recordIDAndReturnFalse.bind(null, CHILD)
|
||||
recordIDAndReturnFalse.bind(null, getInternal(CHILD))
|
||||
);
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, PARENT)
|
||||
recordID.bind(null, getInternal(PARENT))
|
||||
);
|
||||
putListener(
|
||||
GRANDPARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(GRANDPARENT),
|
||||
ON_CLICK_KEY,
|
||||
recordID.bind(null, GRANDPARENT)
|
||||
recordID.bind(null, getInternal(GRANDPARENT))
|
||||
);
|
||||
spyOn(console, 'error');
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
expect(idCallOrder[2]).toBe(GRANDPARENT);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
expect(idCallOrder[1]).toBe(getInternal(PARENT));
|
||||
expect(idCallOrder[2]).toBe(getInternal(GRANDPARENT));
|
||||
expect(console.error.calls.count()).toEqual(0);
|
||||
});
|
||||
|
||||
@@ -333,15 +284,15 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
it('should invoke handlers that were removed while bubbling', () => {
|
||||
var handleParentClick = jest.fn();
|
||||
var handleChildClick = function(event) {
|
||||
deleteAllListeners(PARENT);
|
||||
EventPluginHub.deleteAllListeners(getInternal(PARENT));
|
||||
};
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_CLICK_KEY,
|
||||
handleChildClick
|
||||
);
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_CLICK_KEY,
|
||||
handleParentClick
|
||||
);
|
||||
@@ -352,14 +303,14 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
it('should not invoke newly inserted handlers while bubbling', () => {
|
||||
var handleParentClick = jest.fn();
|
||||
var handleChildClick = function(event) {
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_CLICK_KEY,
|
||||
handleParentClick
|
||||
);
|
||||
};
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_CLICK_KEY,
|
||||
handleChildClick
|
||||
);
|
||||
@@ -368,21 +319,21 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
});
|
||||
|
||||
it('should have mouse enter simulated by test utils', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_MOUSE_ENTER_KEY,
|
||||
recordID.bind(null, CHILD)
|
||||
recordID.bind(null, getInternal(CHILD))
|
||||
);
|
||||
ReactTestUtils.Simulate.mouseEnter(CHILD);
|
||||
expect(idCallOrder.length).toBe(1);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
});
|
||||
|
||||
it('should infer onTouchTap from a touchStart/End', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_TOUCH_TAP_KEY,
|
||||
recordID.bind(null, CHILD)
|
||||
recordID.bind(null, getInternal(CHILD))
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchStart(
|
||||
CHILD,
|
||||
@@ -393,14 +344,14 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
ReactTestUtils.nativeTouchData(0, 0)
|
||||
);
|
||||
expect(idCallOrder.length).toBe(1);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
});
|
||||
|
||||
it('should infer onTouchTap from when dragging below threshold', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_TOUCH_TAP_KEY,
|
||||
recordID.bind(null, CHILD)
|
||||
recordID.bind(null, getInternal(CHILD))
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchStart(
|
||||
CHILD,
|
||||
@@ -411,14 +362,14 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1)
|
||||
);
|
||||
expect(idCallOrder.length).toBe(1);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
});
|
||||
|
||||
it('should not onTouchTap from when dragging beyond threshold', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_TOUCH_TAP_KEY,
|
||||
recordID.bind(null, CHILD)
|
||||
recordID.bind(null, getInternal(CHILD))
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchStart(
|
||||
CHILD,
|
||||
@@ -472,20 +423,20 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
});
|
||||
|
||||
it('should bubble onTouchTap', () => {
|
||||
putListener(
|
||||
CHILD,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(CHILD),
|
||||
ON_TOUCH_TAP_KEY,
|
||||
recordID.bind(null, CHILD)
|
||||
recordID.bind(null, getInternal(CHILD))
|
||||
);
|
||||
putListener(
|
||||
PARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
ON_TOUCH_TAP_KEY,
|
||||
recordID.bind(null, PARENT)
|
||||
recordID.bind(null, getInternal(PARENT))
|
||||
);
|
||||
putListener(
|
||||
GRANDPARENT,
|
||||
EventPluginHub.putListener(
|
||||
getInternal(GRANDPARENT),
|
||||
ON_TOUCH_TAP_KEY,
|
||||
recordID.bind(null, GRANDPARENT)
|
||||
recordID.bind(null, getInternal(GRANDPARENT))
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchStart(
|
||||
CHILD,
|
||||
@@ -496,9 +447,9 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
ReactTestUtils.nativeTouchData(0, 0)
|
||||
);
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
expect(idCallOrder[2]).toBe(GRANDPARENT);
|
||||
expect(idCallOrder[0]).toBe(getInternal(CHILD));
|
||||
expect(idCallOrder[1]).toBe(getInternal(PARENT));
|
||||
expect(idCallOrder[2]).toBe(getInternal(GRANDPARENT));
|
||||
});
|
||||
|
||||
it('should not crash ensureScrollValueMonitoring when createEvent returns null', () => {
|
||||
|
||||
@@ -972,6 +972,61 @@ describe('ReactDOMComponent', () => {
|
||||
expect(tracker.getValue()).toEqual('foo');
|
||||
});
|
||||
|
||||
it('should execute custom event plugin listening behavior', () => {
|
||||
var SimpleEventPlugin = require('SimpleEventPlugin');
|
||||
|
||||
SimpleEventPlugin.didPutListener = jest.fn();
|
||||
SimpleEventPlugin.willDeleteListener = jest.fn();
|
||||
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<div onClick={() => true} />,
|
||||
container
|
||||
);
|
||||
|
||||
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(1);
|
||||
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
|
||||
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle null and missing properly with event hooks', () => {
|
||||
var SimpleEventPlugin = require('SimpleEventPlugin');
|
||||
|
||||
SimpleEventPlugin.didPutListener = jest.fn();
|
||||
SimpleEventPlugin.willDeleteListener = jest.fn();
|
||||
var container = document.createElement('div');
|
||||
|
||||
ReactDOM.render(<div onClick={false} />, container);
|
||||
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(0);
|
||||
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(0);
|
||||
|
||||
ReactDOM.render(<div onClick={null} />, container);
|
||||
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(0);
|
||||
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(0);
|
||||
|
||||
ReactDOM.render(<div onClick={() => 'apple'} />, container);
|
||||
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(1);
|
||||
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(0);
|
||||
|
||||
ReactDOM.render(<div onClick={() => 'banana'} />, container);
|
||||
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(2);
|
||||
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(0);
|
||||
|
||||
ReactDOM.render(<div onClick={null} />, container);
|
||||
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(2);
|
||||
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
|
||||
|
||||
ReactDOM.render(<div />, container);
|
||||
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(2);
|
||||
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
|
||||
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(2);
|
||||
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn for children on void elements', () => {
|
||||
class X extends React.Component {
|
||||
render() {
|
||||
@@ -1102,6 +1157,31 @@ describe('ReactDOMComponent', () => {
|
||||
});
|
||||
|
||||
describe('unmountComponent', () => {
|
||||
it('should clean up listeners', () => {
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
|
||||
var container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
var callback = function() {};
|
||||
var instance = <div onClick={callback} />;
|
||||
instance = ReactDOM.render(instance, container);
|
||||
|
||||
var rootNode = ReactDOM.findDOMNode(instance);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(rootNode);
|
||||
expect(
|
||||
EventPluginHub.getListener(inst, 'onClick')
|
||||
).toBe(callback);
|
||||
expect(rootNode).toBe(ReactDOM.findDOMNode(instance));
|
||||
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
|
||||
expect(
|
||||
EventPluginHub.getListener(inst, 'onClick')
|
||||
).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should clean up input value tracking', () => {
|
||||
var container = document.createElement('div');
|
||||
var node = ReactDOM.render(<input type="text" defaultValue="foo"/>, container);
|
||||
|
||||
@@ -19,6 +19,7 @@ var DOMLazyTree = require('DOMLazyTree');
|
||||
var DOMNamespaces = require('DOMNamespaces');
|
||||
var DOMProperty = require('DOMProperty');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var EventPluginRegistry = require('EventPluginRegistry');
|
||||
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
var ReactDOMComponentFlags = require('ReactDOMComponentFlags');
|
||||
@@ -42,6 +43,7 @@ var warning = require('warning');
|
||||
var didWarnShadyDOM = false;
|
||||
|
||||
var Flags = ReactDOMComponentFlags;
|
||||
var deleteListener = EventPluginHub.deleteListener;
|
||||
var getNode = ReactDOMComponentTree.getNodeFromInstance;
|
||||
var listenTo = ReactBrowserEventEmitter.listenTo;
|
||||
var registrationNameModules = EventPluginRegistry.registrationNameModules;
|
||||
@@ -203,6 +205,30 @@ function assertValidProps(component, props) {
|
||||
);
|
||||
}
|
||||
|
||||
function enqueuePutListener(inst, registrationName, listener, transaction) {
|
||||
if (transaction instanceof ReactServerRenderingTransaction) {
|
||||
return;
|
||||
}
|
||||
if (__DEV__) {
|
||||
// IE8 has no API for event capturing and the `onScroll` event doesn't
|
||||
// bubble.
|
||||
warning(
|
||||
registrationName !== 'onScroll' || isEventSupported('scroll', true),
|
||||
'This browser doesn\'t support the `onScroll` event'
|
||||
);
|
||||
}
|
||||
var containerInfo = inst._hostContainerInfo;
|
||||
var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
|
||||
var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
|
||||
listenTo(registrationName, doc);
|
||||
transaction.getReactMountReady().enqueue(putListener, {
|
||||
inst: inst,
|
||||
registrationName: registrationName,
|
||||
listener: listener,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: This is coming from future #8192. Dedupe this and enqueuePutListener.
|
||||
function ensureListeningTo(inst, registrationName, transaction) {
|
||||
if (transaction instanceof ReactServerRenderingTransaction) {
|
||||
return;
|
||||
@@ -221,6 +247,15 @@ function ensureListeningTo(inst, registrationName, transaction) {
|
||||
listenTo(registrationName, doc);
|
||||
}
|
||||
|
||||
function putListener() {
|
||||
var listenerToPut = this;
|
||||
EventPluginHub.putListener(
|
||||
listenerToPut.inst,
|
||||
listenerToPut.registrationName,
|
||||
listenerToPut.listener
|
||||
);
|
||||
}
|
||||
|
||||
function inputPostMount() {
|
||||
var inst = this;
|
||||
ReactDOMInput.postMountWrapper(inst);
|
||||
@@ -757,7 +792,7 @@ ReactDOMComponent.Mixin = {
|
||||
}
|
||||
if (registrationNameModules.hasOwnProperty(propKey)) {
|
||||
if (propValue) {
|
||||
ensureListeningTo(this, propKey, transaction);
|
||||
enqueuePutListener(this, propKey, propValue, transaction);
|
||||
}
|
||||
} else {
|
||||
if (propKey === STYLE) {
|
||||
@@ -1012,7 +1047,12 @@ ReactDOMComponent.Mixin = {
|
||||
}
|
||||
this._previousStyleCopy = null;
|
||||
} else if (registrationNameModules.hasOwnProperty(propKey)) {
|
||||
// Do nothing for event names.
|
||||
if (lastProps[propKey]) {
|
||||
// Only call deleteListener if there was a listener previously or
|
||||
// else willDeleteListener gets called when there wasn't actually a
|
||||
// listener (e.g., onClick={null})
|
||||
deleteListener(this, propKey);
|
||||
}
|
||||
} else if (isCustomComponent(this._tag, lastProps)) {
|
||||
if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
|
||||
DOMPropertyOperations.deleteValueForAttribute(
|
||||
@@ -1073,7 +1113,9 @@ ReactDOMComponent.Mixin = {
|
||||
}
|
||||
} else if (registrationNameModules.hasOwnProperty(propKey)) {
|
||||
if (nextProp) {
|
||||
ensureListeningTo(this, propKey, transaction);
|
||||
enqueuePutListener(this, propKey, nextProp, transaction);
|
||||
} else if (lastProp) {
|
||||
deleteListener(this, propKey);
|
||||
}
|
||||
} else if (isCustomComponentTag) {
|
||||
if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
|
||||
@@ -1222,6 +1264,7 @@ ReactDOMComponent.Mixin = {
|
||||
|
||||
this.unmountChildren(safely, skipLifecycle);
|
||||
ReactDOMComponentTree.uncacheNode(this);
|
||||
EventPluginHub.deleteAllListeners(this);
|
||||
this._rootNodeID = 0;
|
||||
this._domID = 0;
|
||||
this._wrapperState = null;
|
||||
|
||||
@@ -85,7 +85,15 @@ describe('ReactServerRendering', () => {
|
||||
expect(response).toBe('<!-- react-empty: 1 -->');
|
||||
});
|
||||
|
||||
// TODO: Test that listeners are not registered onto any document/container.
|
||||
it('should not register event listeners', () => {
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var cb = jest.fn();
|
||||
|
||||
ReactServerRendering.renderToString(
|
||||
<span onClick={cb}>hello world</span>
|
||||
);
|
||||
expect(EventPluginHub.__getListenerBank()).toEqual({});
|
||||
});
|
||||
|
||||
it('should render composite components', () => {
|
||||
class Parent extends React.Component {
|
||||
@@ -314,6 +322,16 @@ describe('ReactServerRendering', () => {
|
||||
expect(response).toBe('<span>hello world</span>');
|
||||
});
|
||||
|
||||
it('should not register event listeners', () => {
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var cb = jest.fn();
|
||||
|
||||
ReactServerRendering.renderToStaticMarkup(
|
||||
<span onClick={cb}>hello world</span>
|
||||
);
|
||||
expect(EventPluginHub.__getListenerBank()).toEqual({});
|
||||
});
|
||||
|
||||
it('should only execute certain lifecycle methods', () => {
|
||||
function runTest() {
|
||||
var lifecycle = [];
|
||||
|
||||
@@ -14,12 +14,18 @@
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
var ReactMultiChild = require('ReactMultiChild');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
|
||||
|
||||
var registrationNames = ReactNativeEventEmitter.registrationNames;
|
||||
var putListener = ReactNativeEventEmitter.putListener;
|
||||
var deleteListener = ReactNativeEventEmitter.deleteListener;
|
||||
var deleteAllListeners = ReactNativeEventEmitter.deleteAllListeners;
|
||||
|
||||
type ReactNativeBaseComponentViewConfig = {
|
||||
validAttributes: Object;
|
||||
uiViewClassName: string;
|
||||
@@ -51,6 +57,7 @@ ReactNativeBaseComponent.Mixin = {
|
||||
|
||||
unmountComponent: function(safely, skipLifecycle) {
|
||||
ReactNativeComponentTree.uncacheNode(this);
|
||||
deleteAllListeners(this);
|
||||
this.unmountChildren(safely, skipLifecycle);
|
||||
this._rootNodeID = 0;
|
||||
},
|
||||
@@ -116,9 +123,44 @@ ReactNativeBaseComponent.Mixin = {
|
||||
);
|
||||
}
|
||||
|
||||
this._reconcileListenersUponUpdate(
|
||||
prevElement.props,
|
||||
nextElement.props
|
||||
);
|
||||
this.updateChildren(nextElement.props.children, transaction, context);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {object} initialProps Native component props.
|
||||
*/
|
||||
_registerListenersUponCreation: function(initialProps) {
|
||||
for (var key in initialProps) {
|
||||
// NOTE: The check for `!props[key]`, is only possible because this method
|
||||
// registers listeners the *first* time a component is created.
|
||||
if (registrationNames[key] && initialProps[key]) {
|
||||
var listener = initialProps[key];
|
||||
putListener(this, key, listener);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reconciles event listeners, adding or removing if necessary.
|
||||
* @param {object} prevProps Native component props including events.
|
||||
* @param {object} nextProps Next native component props including events.
|
||||
*/
|
||||
_reconcileListenersUponUpdate: function(prevProps, nextProps) {
|
||||
for (var key in nextProps) {
|
||||
if (registrationNames[key] && (nextProps[key] !== prevProps[key])) {
|
||||
if (nextProps[key]) {
|
||||
putListener(this, key, nextProps[key]);
|
||||
} else {
|
||||
deleteListener(this, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Currently this still uses IDs for reconciliation so this can return null.
|
||||
*
|
||||
@@ -165,6 +207,7 @@ ReactNativeBaseComponent.Mixin = {
|
||||
|
||||
ReactNativeComponentTree.precacheNode(this, tag);
|
||||
|
||||
this._registerListenersUponCreation(this._currentElement.props);
|
||||
this.initializeChildren(
|
||||
this._currentElement.props.children,
|
||||
tag,
|
||||
|
||||
@@ -78,14 +78,29 @@ var removeTouchesAtIndices = function(
|
||||
return rippedOut;
|
||||
};
|
||||
|
||||
/**
|
||||
* `ReactNativeEventEmitter` is used to attach top-level event listeners. For example:
|
||||
*
|
||||
* ReactNativeEventEmitter.putListener('myID', 'onClick', myFunction);
|
||||
*
|
||||
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
var ReactNativeEventEmitter = {
|
||||
|
||||
...ReactEventEmitterMixin,
|
||||
|
||||
registrationNames: EventPluginRegistry.registrationNameModules,
|
||||
|
||||
putListener: EventPluginHub.putListener,
|
||||
|
||||
getListener: EventPluginHub.getListener,
|
||||
|
||||
deleteListener: EventPluginHub.deleteListener,
|
||||
|
||||
deleteAllListeners: EventPluginHub.deleteAllListeners,
|
||||
|
||||
/**
|
||||
* Internal version of `receiveEvent` in terms of normalized (non-tag)
|
||||
* `rootNodeID`.
|
||||
|
||||
@@ -19,6 +19,11 @@ var accumulateInto = require('accumulateInto');
|
||||
var forEachAccumulated = require('forEachAccumulated');
|
||||
var invariant = require('invariant');
|
||||
|
||||
/**
|
||||
* Internal store for event listeners
|
||||
*/
|
||||
var listenerBank = {};
|
||||
|
||||
/**
|
||||
* Internal queue of events that have accumulated their dispatches and are
|
||||
* waiting to have their dispatches executed.
|
||||
@@ -48,6 +53,12 @@ var executeDispatchesAndReleaseTopLevel = function(e) {
|
||||
return executeDispatchesAndRelease(e, false);
|
||||
};
|
||||
|
||||
var getDictionaryKey = function(inst) {
|
||||
// Prevents V8 performance issue:
|
||||
// https://github.com/facebook/react/pull/7232
|
||||
return '.' + inst._rootNodeID;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a unified interface for event plugins to be installed and configured.
|
||||
*
|
||||
@@ -90,22 +101,88 @@ var EventPluginHub = {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores `listener` at `listenerBank[registrationName][key]`. Is idempotent.
|
||||
*
|
||||
* @param {object} inst The instance, which is the source of events.
|
||||
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
||||
* @param {function} listener The callback to store.
|
||||
*/
|
||||
putListener: function(inst, registrationName, listener) {
|
||||
invariant(
|
||||
typeof listener === 'function',
|
||||
'Expected %s listener to be a function, instead got type %s',
|
||||
registrationName, typeof listener
|
||||
);
|
||||
|
||||
var key = getDictionaryKey(inst);
|
||||
var bankForRegistrationName =
|
||||
listenerBank[registrationName] || (listenerBank[registrationName] = {});
|
||||
bankForRegistrationName[key] = listener;
|
||||
|
||||
var PluginModule =
|
||||
EventPluginRegistry.registrationNameModules[registrationName];
|
||||
if (PluginModule && PluginModule.didPutListener) {
|
||||
PluginModule.didPutListener(inst, registrationName, listener);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {object} inst The instance, which is the source of events.
|
||||
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
||||
* @return {?function} The stored callback.
|
||||
*/
|
||||
getListener: function(inst, registrationName) {
|
||||
var listener = inst._currentElement.props[registrationName];
|
||||
if (listener !== undefined) {
|
||||
invariant(
|
||||
typeof listener === 'function',
|
||||
'Expected %s listener to be a function, instead got type %s',
|
||||
registrationName,
|
||||
typeof listener
|
||||
);
|
||||
var bankForRegistrationName = listenerBank[registrationName];
|
||||
var key = getDictionaryKey(inst);
|
||||
return bankForRegistrationName && bankForRegistrationName[key];
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes a listener from the registration bank.
|
||||
*
|
||||
* @param {object} inst The instance, which is the source of events.
|
||||
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
||||
*/
|
||||
deleteListener: function(inst, registrationName) {
|
||||
var PluginModule =
|
||||
EventPluginRegistry.registrationNameModules[registrationName];
|
||||
if (PluginModule && PluginModule.willDeleteListener) {
|
||||
PluginModule.willDeleteListener(inst, registrationName);
|
||||
}
|
||||
|
||||
var bankForRegistrationName = listenerBank[registrationName];
|
||||
// TODO: This should never be null -- when is it?
|
||||
if (bankForRegistrationName) {
|
||||
var key = getDictionaryKey(inst);
|
||||
delete bankForRegistrationName[key];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes all listeners for the DOM element with the supplied ID.
|
||||
*
|
||||
* @param {object} inst The instance, which is the source of events.
|
||||
*/
|
||||
deleteAllListeners: function(inst) {
|
||||
var key = getDictionaryKey(inst);
|
||||
for (var registrationName in listenerBank) {
|
||||
if (!listenerBank.hasOwnProperty(registrationName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!listenerBank[registrationName][key]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var PluginModule =
|
||||
EventPluginRegistry.registrationNameModules[registrationName];
|
||||
if (PluginModule && PluginModule.willDeleteListener) {
|
||||
PluginModule.willDeleteListener(inst, registrationName);
|
||||
}
|
||||
|
||||
delete listenerBank[registrationName][key];
|
||||
}
|
||||
return listener;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -183,6 +260,17 @@ var EventPluginHub = {
|
||||
ReactErrorUtils.rethrowCaughtError();
|
||||
},
|
||||
|
||||
/**
|
||||
* These are needed for tests only. Do not use!
|
||||
*/
|
||||
__purge: function() {
|
||||
listenerBank = {};
|
||||
},
|
||||
|
||||
__getListenerBank: function() {
|
||||
return listenerBank;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = EventPluginHub;
|
||||
|
||||
@@ -130,7 +130,8 @@ function publishEventForPlugin(
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a registration name that is used to identify dispatched events.
|
||||
* Publishes a registration name that is used to identify dispatched events and
|
||||
* can be used with `EventPluginHub.putListener` to register listeners.
|
||||
*
|
||||
* @param {string} registrationName Registration name to add.
|
||||
* @param {object} PluginModule Plugin publishing the event.
|
||||
|
||||
@@ -36,5 +36,14 @@ export type PluginModule<NativeEvent> = {
|
||||
nativeTarget: NativeEvent,
|
||||
nativeEventTarget: EventTarget,
|
||||
) => null | ReactSyntheticEvent,
|
||||
didPutListener?: (
|
||||
inst: ReactInstance,
|
||||
registrationName: string,
|
||||
listener: () => void,
|
||||
) => void,
|
||||
willDeleteListener?: (
|
||||
inst: ReactInstance,
|
||||
registrationName: string,
|
||||
) => void,
|
||||
tapMoveThreshold?: number,
|
||||
};
|
||||
|
||||
@@ -14,21 +14,19 @@
|
||||
jest.mock('isEventSupported');
|
||||
|
||||
describe('EventPluginHub', () => {
|
||||
var React;
|
||||
var ReactTestUtils;
|
||||
var EventPluginHub;
|
||||
var isEventSupported;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModuleRegistry();
|
||||
React = require('React');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
EventPluginHub = require('EventPluginHub');
|
||||
isEventSupported = require('isEventSupported');
|
||||
isEventSupported.mockReturnValueOnce(false);
|
||||
});
|
||||
|
||||
it('should prevent non-function listeners, at dispatch', () => {
|
||||
var node = ReactTestUtils.renderIntoDocument(
|
||||
<div onClick="not a function" />
|
||||
);
|
||||
it('should prevent non-function listeners', () => {
|
||||
expect(function() {
|
||||
ReactTestUtils.SimulateNative.click(node);
|
||||
EventPluginHub.putListener(1, 'onClick', 'not a function');
|
||||
}).toThrowError(
|
||||
'Expected onClick listener to be a function, instead got type string'
|
||||
);
|
||||
|
||||
+8
-38
@@ -233,7 +233,7 @@ var registerTestHandlers = function(eventTestConfig, readableIDToID) {
|
||||
}
|
||||
return config.returnVal;
|
||||
}.bind(null, readableID, nodeConfig);
|
||||
putListener(getInstanceFromNode(id), registrationName, handler);
|
||||
EventPluginHub.putListener(getInstanceFromNode(id), registrationName, handler);
|
||||
}
|
||||
};
|
||||
for (var eventName in eventTestConfig) {
|
||||
@@ -258,6 +258,9 @@ var registerTestHandlers = function(eventTestConfig, readableIDToID) {
|
||||
return runs;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
var run = function(config, hierarchyConfig, nativeEventConfig) {
|
||||
var max = NA;
|
||||
var searchForMax = function(nodeConfig) {
|
||||
@@ -306,30 +309,10 @@ var PARENT_HOST_NODE = { };
|
||||
var CHILD_HOST_NODE = { };
|
||||
var CHILD_HOST_NODE2 = { };
|
||||
|
||||
var GRANDPARENT_INST = {
|
||||
_hostParent: null,
|
||||
_rootNodeID: '1',
|
||||
_hostNode: GRANDPARENT_HOST_NODE,
|
||||
_currentElement: { props: {} },
|
||||
};
|
||||
var PARENT_INST = {
|
||||
_hostParent: GRANDPARENT_INST,
|
||||
_rootNodeID: '2',
|
||||
_hostNode: PARENT_HOST_NODE,
|
||||
_currentElement: { props: {} },
|
||||
};
|
||||
var CHILD_INST = {
|
||||
_hostParent: PARENT_INST,
|
||||
_rootNodeID: '3',
|
||||
_hostNode: CHILD_HOST_NODE,
|
||||
_currentElement: { props: {} },
|
||||
};
|
||||
var CHILD_INST2 = {
|
||||
_hostParent: PARENT_INST,
|
||||
_rootNodeID: '4',
|
||||
_hostNode: CHILD_HOST_NODE2,
|
||||
_currentElement: { props: {} },
|
||||
};
|
||||
var GRANDPARENT_INST = { _hostParent: null, _rootNodeID: '1', _hostNode: GRANDPARENT_HOST_NODE };
|
||||
var PARENT_INST = { _hostParent: GRANDPARENT_INST, _rootNodeID: '2', _hostNode: PARENT_HOST_NODE };
|
||||
var CHILD_INST = { _hostParent: PARENT_INST, _rootNodeID: '3', _hostNode: CHILD_HOST_NODE };
|
||||
var CHILD_INST2 = { _hostParent: PARENT_INST, _rootNodeID: '4', _hostNode: CHILD_HOST_NODE2 };
|
||||
|
||||
GRANDPARENT_HOST_NODE._reactInstance = GRANDPARENT_INST;
|
||||
PARENT_HOST_NODE._reactInstance = PARENT_INST;
|
||||
@@ -356,14 +339,6 @@ function getNodeFromInstance(inst) {
|
||||
return inst._hostNode;
|
||||
}
|
||||
|
||||
function putListener(node, registrationName, handler) {
|
||||
node._currentElement.props[registrationName] = handler;
|
||||
}
|
||||
|
||||
function deleteAllListeners(node) {
|
||||
node._currentElement.props = {};
|
||||
}
|
||||
|
||||
describe('ResponderEventPlugin', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -373,11 +348,6 @@ describe('ResponderEventPlugin', () => {
|
||||
EventPluginUtils = require('EventPluginUtils');
|
||||
ResponderEventPlugin = require('ResponderEventPlugin');
|
||||
|
||||
deleteAllListeners(GRANDPARENT_INST);
|
||||
deleteAllListeners(PARENT_INST);
|
||||
deleteAllListeners(CHILD_INST);
|
||||
deleteAllListeners(CHILD_INST2);
|
||||
|
||||
EventPluginUtils.injection.injectComponentTree({
|
||||
getInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
|
||||
@@ -879,6 +879,26 @@ describe('ReactErrorBoundaries', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not register event handlers for unmounted children', () => {
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var container = document.createElement('div');
|
||||
EventPluginHub.putListener = jest.fn();
|
||||
ReactDOM.render(
|
||||
<ErrorBoundary>
|
||||
<button onClick={() => {}}>Click me</button>
|
||||
<BrokenRender />
|
||||
</ErrorBoundary>,
|
||||
container
|
||||
);
|
||||
expect(EventPluginHub.putListener).not.toBeCalled();
|
||||
|
||||
log.length = 0;
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
expect(log).toEqual([
|
||||
'ErrorBoundary componentWillUnmount',
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not call componentWillUnmount when aborting initial mount', () => {
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
|
||||
Reference in New Issue
Block a user