mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Adds React event component and React event target support to SSR renderer (#15242)
* Adds React event component and React event target support to SSR renderer
This commit is contained in:
@@ -22,6 +22,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import {
|
||||
warnAboutDeprecatedLifecycles,
|
||||
enableSuspenseServerRenderer,
|
||||
enableEventAPI,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {
|
||||
@@ -36,6 +37,8 @@ import {
|
||||
REACT_CONTEXT_TYPE,
|
||||
REACT_LAZY_TYPE,
|
||||
REACT_MEMO_TYPE,
|
||||
REACT_EVENT_COMPONENT_TYPE,
|
||||
REACT_EVENT_TARGET_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
|
||||
import {
|
||||
@@ -1162,6 +1165,32 @@ class ReactDOMServerRenderer {
|
||||
this.stack.push(frame);
|
||||
return '';
|
||||
}
|
||||
case REACT_EVENT_COMPONENT_TYPE:
|
||||
case REACT_EVENT_TARGET_TYPE: {
|
||||
if (enableEventAPI) {
|
||||
const nextChildren = toArray(
|
||||
((nextChild: any): ReactElement).props.children,
|
||||
);
|
||||
const frame: Frame = {
|
||||
type: null,
|
||||
domNamespace: parentNamespace,
|
||||
children: nextChildren,
|
||||
childIndex: 0,
|
||||
context: context,
|
||||
footer: '',
|
||||
};
|
||||
if (__DEV__) {
|
||||
((frame: any): FrameDev).debugElementStack = [];
|
||||
}
|
||||
this.stack.push(frame);
|
||||
return '';
|
||||
}
|
||||
invariant(
|
||||
false,
|
||||
'ReactDOMServer does not yet support the event API.',
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line-no-fallthrough
|
||||
case REACT_LAZY_TYPE:
|
||||
invariant(
|
||||
false,
|
||||
|
||||
+221
-1
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -16,6 +15,8 @@ let Scheduler;
|
||||
let ReactFeatureFlags;
|
||||
let EventComponent;
|
||||
let ReactTestRenderer;
|
||||
let ReactDOM;
|
||||
let ReactDOMServer;
|
||||
let EventTarget;
|
||||
let ReactEvents;
|
||||
|
||||
@@ -51,6 +52,16 @@ function initTestRenderer() {
|
||||
ReactTestRenderer = require('react-test-renderer');
|
||||
}
|
||||
|
||||
function initReactDOM() {
|
||||
init();
|
||||
ReactDOM = require('react-dom');
|
||||
}
|
||||
|
||||
function initReactDOMServer() {
|
||||
init();
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
}
|
||||
|
||||
// This is a new feature in Fiber so I put it in its own test file. It could
|
||||
// probably move to one of the other test files once it is official.
|
||||
describe('ReactFiberEvents', () => {
|
||||
@@ -358,4 +369,213 @@ describe('ReactFiberEvents', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReactDOM', () => {
|
||||
beforeEach(() => {
|
||||
initReactDOM();
|
||||
EventComponent = createReactEventComponent();
|
||||
EventTarget = ReactEvents.TouchHitTarget;
|
||||
});
|
||||
|
||||
it('should render a simple event component with a single child', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>Hello world</div>
|
||||
</EventComponent>
|
||||
);
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe('<div>Hello world</div>');
|
||||
});
|
||||
|
||||
it('should warn when an event component has a direct text child', () => {
|
||||
const Test = () => <EventComponent>Hello world</EventComponent>;
|
||||
|
||||
expect(() => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn when an event component has a direct text child #2', () => {
|
||||
const ChildWrapper = () => 'Hello world';
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<ChildWrapper />
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a simple event component with a single event target', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>Hello world</div>
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe('<div>Hello world</div>');
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>I am now a span</span>
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
ReactDOM.render(<Test2 />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe('<span>I am now a span</span>');
|
||||
});
|
||||
|
||||
it('should warn when an event target has a direct text child', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>Hello world</EventTarget>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev([
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
'Warning: <TouchHitTarget> must have a single DOM element as a child. Found no children.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should warn when an event target has a direct text child #2', () => {
|
||||
const ChildWrapper = () => 'Hello world';
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<ChildWrapper />
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev([
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
'Warning: <TouchHitTarget> must have a single DOM element as a child. Found no children.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should warn when an event target has more than one child', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>Child 1</span>
|
||||
<span>Child 2</span>
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
expect(() => {
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: <TouchHitTarget> must only have a single DOM element as a child. Found many children.',
|
||||
);
|
||||
// This should not fire a warning, as this is now valid.
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>Child 1</span>
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
);
|
||||
ReactDOM.render(<Test2 />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe('<span>Child 1</span>');
|
||||
});
|
||||
|
||||
it('should warn if an event target is not a direct child of an event component', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<EventTarget>
|
||||
<span>Child 1</span>
|
||||
</EventTarget>
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets must be direct children of event components.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReactDOMServer', () => {
|
||||
beforeEach(() => {
|
||||
initReactDOMServer();
|
||||
EventComponent = createReactEventComponent();
|
||||
EventTarget = ReactEvents.TouchHitTarget;
|
||||
});
|
||||
|
||||
it('should render a simple event component with a single child', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>Hello world</div>
|
||||
</EventComponent>
|
||||
);
|
||||
const output = ReactDOMServer.renderToString(<Test />);
|
||||
expect(output).toBe('<div>Hello world</div>');
|
||||
});
|
||||
|
||||
it('should render a simple event component with a single event target', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>Hello world</div>
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
let output = ReactDOMServer.renderToString(<Test />);
|
||||
expect(output).toBe('<div>Hello world</div>');
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>I am now a span</span>
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
output = ReactDOMServer.renderToString(<Test2 />);
|
||||
expect(output).toBe('<span>I am now a span</span>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user