diff --git a/packages/react-reconciler/src/__tests__/useEvent-test.internal.js b/packages/react-reconciler/src/__tests__/useEvent-test.internal.js
index 65f266846b3cc..379d22e12ad06 100644
--- a/packages/react-reconciler/src/__tests__/useEvent-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/useEvent-test.internal.js
@@ -19,10 +19,13 @@ describe('useEvent', () => {
let ReactNoop;
let Scheduler;
let act;
+ let createContext;
+ let useContext;
let useState;
let useEvent;
let useEffect;
let useLayoutEffect;
+ let useMemo;
beforeEach(() => {
React = require('react');
@@ -30,10 +33,13 @@ describe('useEvent', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
+ createContext = React.createContext;
+ useContext = React.useContext;
useState = React.useState;
useEvent = React.experimental_useEvent;
useEffect = React.useEffect;
useLayoutEffect = React.useLayoutEffect;
+ useMemo = React.useMemo;
});
function span(prop) {
@@ -150,7 +156,7 @@ describe('useEvent', () => {
});
// @gate enableUseEventHook
- it('useLayoutEffect shouldn’t re-fire when event handlers change', () => {
+ it("useLayoutEffect shouldn't re-fire when event handlers change", () => {
class IncrementButton extends React.PureComponent {
increment = () => {
this.props.onClick();
@@ -235,7 +241,7 @@ describe('useEvent', () => {
});
// @gate enableUseEventHook
- it('useEffect shouldn’t re-fire when event handlers change', () => {
+ it("useEffect shouldn't re-fire when event handlers change", () => {
class IncrementButton extends React.PureComponent {
increment = () => {
this.props.onClick();
@@ -431,4 +437,159 @@ describe('useEvent', () => {
act(() => ReactNoop.render());
expect(Scheduler).toHaveYielded(['Effect value: 2', 'Event value: 2']);
});
+
+ // @gate enableUseEventHook
+ it('integration: implements docs chat room example', () => {
+ function createConnection() {
+ let connectedCallback;
+ let timeout;
+ return {
+ connect() {
+ timeout = setTimeout(() => {
+ if (connectedCallback) {
+ connectedCallback();
+ }
+ }, 100);
+ },
+ on(event, callback) {
+ if (connectedCallback) {
+ throw Error('Cannot add the handler twice.');
+ }
+ if (event !== 'connected') {
+ throw Error('Only "connected" event is supported.');
+ }
+ connectedCallback = callback;
+ },
+ disconnect() {
+ clearTimeout(timeout);
+ },
+ };
+ }
+
+ function ChatRoom({roomId, theme}) {
+ const onConnected = useEvent(() => {
+ Scheduler.unstable_yieldValue('Connected! theme: ' + theme);
+ });
+
+ useEffect(() => {
+ const connection = createConnection(roomId);
+ connection.on('connected', () => {
+ onConnected();
+ });
+ connection.connect();
+ return () => connection.disconnect();
+ }, [roomId, onConnected]);
+
+ return ;
+ }
+
+ act(() => ReactNoop.render());
+ expect(Scheduler).toHaveYielded(['Welcome to the general room!']);
+ expect(ReactNoop.getChildren()).toEqual([
+ span('Welcome to the general room!'),
+ ]);
+
+ jest.advanceTimersByTime(100);
+ Scheduler.unstable_advanceTime(100);
+ expect(Scheduler).toHaveYielded(['Connected! theme: light']);
+
+ // change roomId only
+ act(() => ReactNoop.render());
+ expect(Scheduler).toHaveYielded(['Welcome to the music room!']);
+ expect(ReactNoop.getChildren()).toEqual([
+ span('Welcome to the music room!'),
+ ]);
+ jest.advanceTimersByTime(100);
+ Scheduler.unstable_advanceTime(100);
+ // should trigger a reconnect
+ expect(Scheduler).toHaveYielded(['Connected! theme: light']);
+
+ // change theme only
+ act(() => ReactNoop.render());
+ expect(Scheduler).toHaveYielded(['Welcome to the music room!']);
+ expect(ReactNoop.getChildren()).toEqual([
+ span('Welcome to the music room!'),
+ ]);
+ jest.advanceTimersByTime(100);
+ Scheduler.unstable_advanceTime(100);
+ // should not trigger a reconnect
+ expect(Scheduler).toFlushWithoutYielding();
+
+ // change roomId only
+ act(() => ReactNoop.render());
+ expect(Scheduler).toHaveYielded(['Welcome to the travel room!']);
+ expect(ReactNoop.getChildren()).toEqual([
+ span('Welcome to the travel room!'),
+ ]);
+ jest.advanceTimersByTime(100);
+ Scheduler.unstable_advanceTime(100);
+ // should trigger a reconnect
+ expect(Scheduler).toHaveYielded(['Connected! theme: dark']);
+ });
+
+ // @gate enableUseEventHook
+ it('integration: implements the docs logVisit example', () => {
+ class AddToCartButton extends React.PureComponent {
+ addToCart = () => {
+ this.props.onClick();
+ };
+ render() {
+ return ;
+ }
+ }
+ const ShoppingCartContext = createContext(null);
+
+ function AppShell({children}) {
+ const [items, updateItems] = useState([]);
+ const value = useMemo(() => ({items, updateItems}), [items, updateItems]);
+
+ return (
+
+ {children}
+
+ );
+ }
+
+ function Page({url}) {
+ const {items, updateItems} = useContext(ShoppingCartContext);
+ const onClick = useEvent(() => updateItems([...items, 1]));
+ const numberOfItems = items.length;
+
+ const onVisit = useEvent(visitedUrl => {
+ Scheduler.unstable_yieldValue(
+ 'url: ' + url + ', numberOfItems: ' + numberOfItems,
+ );
+ });
+
+ useEffect(() => {
+ onVisit(url);
+ }, [url]);
+
+ return ;
+ }
+
+ const button = React.createRef(null);
+ act(() =>
+ ReactNoop.render(
+
+
+ ,
+ ),
+ );
+ expect(Scheduler).toHaveYielded([
+ 'Add to cart',
+ 'url: /shop/1, numberOfItems: 0',
+ ]);
+ act(button.current.addToCart);
+ expect(Scheduler).toFlushWithoutYielding();
+
+ act(() =>
+ ReactNoop.render(
+
+
+ ,
+ ),
+ );
+ expect(Scheduler).toHaveYielded(['url: /shop/2, numberOfItems: 1']);
+ });
});