Skip to content

Commit

Permalink
State update bug in concurrent mode (#14698)
Browse files Browse the repository at this point in the history
* State update bug in concurrent mode

* Fix bug introduced by double-rendering Functions using hooks
  • Loading branch information
bvaughn authored and acdlite committed Jan 25, 2019
1 parent e679a4b commit f11a9c1
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 3 deletions.
103 changes: 103 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMHooks-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/

'use strict';

let React;
let ReactDOM;

describe('ReactDOMSuspensePlaceholder', () => {
let container;

beforeEach(() => {
jest.resetModules();

React = require('react');
ReactDOM = require('react-dom');

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

afterEach(() => {
document.body.removeChild(container);
});

it('should not bail out when an update is scheduled from within an event handler', () => {
const {createRef, useCallback, useState} = React;

const Example = ({inputRef, labelRef}) => {
const [text, setText] = useState('');
const handleInput = useCallback(event => {
setText(event.target.value);
});

return (
<React.Fragment>
<input ref={inputRef} onInput={handleInput} />
<label ref={labelRef}>{text}</label>
</React.Fragment>
);
};

const inputRef = createRef();
const labelRef = createRef();

ReactDOM.render(
<Example inputRef={inputRef} labelRef={labelRef} />,
container,
);

inputRef.current.value = 'abc';
inputRef.current.dispatchEvent(
new Event('input', {bubbles: true, cancelable: true}),
);

expect(labelRef.current.innerHTML).toBe('abc');
});

it('should not bail out when an update is scheduled from within an event handler in ConcurrentMode', () => {
const {createRef, useCallback, useState} = React;

const Example = ({inputRef, labelRef}) => {
const [text, setText] = useState('');
const handleInput = useCallback(event => {
setText(event.target.value);
});

return (
<React.Fragment>
<input ref={inputRef} onInput={handleInput} />
<label ref={labelRef}>{text}</label>
</React.Fragment>
);
};

const inputRef = createRef();
const labelRef = createRef();

const root = ReactDOM.unstable_createRoot(container);
root.render(
<React.unstable_ConcurrentMode>
<Example inputRef={inputRef} labelRef={labelRef} />
</React.unstable_ConcurrentMode>,
);

jest.runAllTimers();

inputRef.current.value = 'abc';
inputRef.current.dispatchEvent(
new Event('input', {bubbles: true, cancelable: true}),
);

jest.runAllTimers();

expect(labelRef.current.innerHTML).toBe('abc');
});
});
6 changes: 3 additions & 3 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ function updateForwardRef(
) {
// Only double-render components with Hooks
if (workInProgress.memoizedState !== null) {
renderWithHooks(
nextChildren = renderWithHooks(
current,
workInProgress,
render,
Expand Down Expand Up @@ -567,7 +567,7 @@ function updateFunctionComponent(
) {
// Only double-render components with Hooks
if (workInProgress.memoizedState !== null) {
renderWithHooks(
nextChildren = renderWithHooks(
current,
workInProgress,
Component,
Expand Down Expand Up @@ -1252,7 +1252,7 @@ function mountIndeterminateComponent(
) {
// Only double-render components with Hooks
if (workInProgress.memoizedState !== null) {
renderWithHooks(
value = renderWithHooks(
null,
workInProgress,
Component,
Expand Down

0 comments on commit f11a9c1

Please sign in to comment.