From b62f851e64066770e7e7929372ced460a1784364 Mon Sep 17 00:00:00 2001 From: Giamir Buoncristiani Date: Sun, 26 Mar 2017 17:24:26 +0100 Subject: [PATCH 1/7] Add a button to switch from Fiber with time-slicing to Fiber without it --- examples/fiber/index.html | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/examples/fiber/index.html b/examples/fiber/index.html index 7a56ca0b9d..a62f2bfcaf 100644 --- a/examples/fiber/index.html +++ b/examples/fiber/index.html @@ -126,16 +126,29 @@ class ExampleApplication extends React.Component { constructor() { super(); - this.state = { seconds: 0 }; + this.state = { + seconds: 0, + useTimeSlicing: true + }; this.tick = this.tick.bind(this); + this.toogleTimeSlicing = this.toogleTimeSlicing.bind(this); } componentDidMount() { this.intervalID = setInterval(this.tick, 1000); } tick() { - ReactDOMFiber.unstable_deferredUpdates(() => - this.setState(state => ({ seconds: (state.seconds % 10) + 1 })) - ); + if (this.state.useTimeSlicing) { + // Update is time-sliced. + ReactDOMFiber.unstable_deferredUpdates(() => { + this.setState(state => ({ ...state, seconds: (state.seconds % 10) + 1 })); + }); + } else { + // Update is not time-sliced. Causes demo to stutter. + this.setState(state => ({ ...state, seconds: (state.seconds % 10) + 1 })); + } + } + toogleTimeSlicing() { + this.setState(state => ({ ...state, useTimeSlicing: !state.useTimeSlicing})); } componentWillUnmount() { clearInterval(this.intervalID); @@ -147,11 +160,14 @@ const scale = 1 + (t > 5 ? 10 - t : t) / 10; const transform = 'scaleX(' + (scale / 2.1) + ') scaleY(0.7) translateZ(0.1px)'; return ( -
-
- - {this.state.seconds} - +
+ +
+
+ + {this.state.seconds} + +
); From 29d9710892f773b4d5081f1b77820d40d79f69d3 Mon Sep 17 00:00:00 2001 From: Nathan Hunzaker Date: Mon, 27 Mar 2017 12:39:18 -0400 Subject: [PATCH 2/7] Fix Chrome number input backspace and invalid input issue (#7359) * Only re-assign defaultValue if it is different * Do not set value if it is the same * Properly cover defaultValue * Use coercion to be smart about value assignment * Add explanation of loose type checks in value assignment. * Add test coverage for setAttribute update. * Only apply loose value check to text inputs * Fix case where empty switches to zero * Handle zero case in controlled input * Correct mistake with default value assignment after rebase * Do not assign bad input to number input * Only trigger number input value attribute updates on blur * Remove reference to LinkedValueUtils * Record new fiber tests * Add tests for blurred number input behavior * Replace onBlur wrapper with rule in ChangeEventPlugin * Sift down to only number inputs * Re-record fiber tests * Add test case for updating attribute on uncontrolled inputs. Make related correction * Handle uncontrolled inputs, integrate fiber * Reorder boolean to mitigate DOM checks * Only assign value if it is different * Add number input browser test fixtures During the course of the number input fix, we uncovered many edge cases. This commit adds browser test fixtures for each of those instances. * Address edge case preventing number precision lower than 1 place 0.0 coerces to 0, however they are not the same value when doing string comparision. This prevented controlled number inputs from inputing the characters `0.00`. Also adds test cases. * Accommodate lack of IE9 number input support IE9 does not support number inputs. Number inputs in IE9 fallback to traditional text inputs. This means that accessing `input.value` will report the raw text, rather than parsing a numeric value. This commit makes the ReactDOMInput wrapper check to see if the `type` prop has been configured to `"number"`. In those cases, it will perform a comparison based upon `parseFloat` instead of the raw input value. * Remove footnotes about IE exponent issues With the recent IE9 fix, IE properly inserts `e` when it produces an invalid number. * Address exception in IE9/10 ChangeEventPlugin blur event On blur, inputs have their values assigned. This is so that number inputs do not conduct unexpected behavior in Chrome/Safari. Unfortunately, there are cases where the target instance might be undefined in IE9/10, raising an exception. * Migrate over ReactDOMInput.js number input fixes to Fiber Also re-record tests * Update number fixtures to use latest components * Add number input test case for dashes and negative numbers * Replace trailing dash test case with replace with dash Also run prettier --- fixtures/dom/src/components/Header.js | 1 + fixtures/dom/src/components/fixtures/index.js | 3 + .../fixtures/number-inputs/NumberTestCase.js | 37 ++++ .../fixtures/number-inputs/index.js | 167 ++++++++++++++++++ scripts/fiber/tests-passing.txt | 11 ++ .../dom/fiber/wrappers/ReactDOMFiberInput.js | 28 +-- .../dom/shared/HTMLDOMPropertyConfig.js | 28 +++ .../__tests__/DOMPropertyOperations-test.js | 29 +++ .../shared/eventPlugins/ChangeEventPlugin.js | 25 +++ .../wrappers/__tests__/ReactDOMInput-test.js | 126 +++++++++++++ .../stack/client/wrappers/ReactDOMInput.js | 28 +-- 11 files changed, 463 insertions(+), 20 deletions(-) create mode 100644 fixtures/dom/src/components/fixtures/number-inputs/NumberTestCase.js create mode 100644 fixtures/dom/src/components/fixtures/number-inputs/index.js diff --git a/fixtures/dom/src/components/Header.js b/fixtures/dom/src/components/Header.js index 83f77eee31..5f717d8be1 100644 --- a/fixtures/dom/src/components/Header.js +++ b/fixtures/dom/src/components/Header.js @@ -44,6 +44,7 @@ const Header = React.createClass({ + diff --git a/fixtures/dom/src/components/fixtures/index.js b/fixtures/dom/src/components/fixtures/index.js index ea7cebbde8..516e8ad3ae 100644 --- a/fixtures/dom/src/components/fixtures/index.js +++ b/fixtures/dom/src/components/fixtures/index.js @@ -4,6 +4,7 @@ import TextInputFixtures from './text-inputs'; import SelectFixtures from './selects'; import TextAreaFixtures from './textareas'; import InputChangeEvents from './input-change-events'; +import NumberInputFixtures from './number-inputs/'; /** * A simple routing component that renders the appropriate @@ -22,6 +23,8 @@ const FixturesPage = React.createClass({ return ; case '/input-change-events': return ; + case '/number-inputs': + return ; default: return

Please select a test fixture.

; } diff --git a/fixtures/dom/src/components/fixtures/number-inputs/NumberTestCase.js b/fixtures/dom/src/components/fixtures/number-inputs/NumberTestCase.js new file mode 100644 index 0000000000..2c072d478e --- /dev/null +++ b/fixtures/dom/src/components/fixtures/number-inputs/NumberTestCase.js @@ -0,0 +1,37 @@ +const React = window.React; + +import Fixture from '../../Fixture'; + +const NumberTestCase = React.createClass({ + getInitialState() { + return { value: '' }; + }, + onChange(event) { + const parsed = parseFloat(event.target.value, 10) + const value = isNaN(parsed) ? '' : parsed + + this.setState({ value }) + }, + render() { + return ( + +
{this.props.children}
+ +
+
+ Controlled + + Value: {JSON.stringify(this.state.value)} +
+ +
+ Uncontrolled + +
+
+
+ ); + }, +}); + +export default NumberTestCase; diff --git a/fixtures/dom/src/components/fixtures/number-inputs/index.js b/fixtures/dom/src/components/fixtures/number-inputs/index.js new file mode 100644 index 0000000000..2c88c333ed --- /dev/null +++ b/fixtures/dom/src/components/fixtures/number-inputs/index.js @@ -0,0 +1,167 @@ +const React = window.React; + +import FixtureSet from '../../FixtureSet'; +import TestCase from '../../TestCase'; +import NumberTestCase from './NumberTestCase'; + +const NumberInputs = React.createClass({ + render() { + return ( + + + +
  • Type "3.1"
  • +
  • Press backspace, eliminating the "1"
  • +
    + + + The field should read "3.", preserving the decimal place + + + + +

    + Notes: Chrome and Safari clear trailing + decimals on blur. React makes this concession so that the + value attribute remains in sync with the value property. +

    +
    + + + +
  • Type "0.01"
  • +
    + + + The field should read "0.01" + + + +
    + + + +
  • Type "2e"
  • +
  • Type 4, to read "2e4"
  • +
    + + + The field should read "2e4". The parsed value should read "20000" + + + +
    + + + +
  • Type "3.14"
  • +
  • Press "e", so that the input reads "3.14e"
  • +
    + + + The field should read "3.14e", the parsed value should be empty + + + +
    + + + +
  • Type "3.14"
  • +
  • Move the text cursor to after the decimal place
  • +
  • Press "e" twice, so that the value reads "3.ee14"
  • +
    + + + The field should read "3.ee14" + + + +
    + + + +
  • Type "3.0"
  • +
    + + + The field should read "3.0" + + + +
    + + + +
  • Type "300"
  • +
  • Move the cursor to after the "3"
  • +
  • Type "."
  • +
    + + + The field should read "3.00", not "3" + + +
    + + + +
  • Type "3"
  • +
  • Select the entire value"
  • +
  • Type '-' to replace '3' with '-'
  • +
    + + + The field should read "-", not be blank. + + +
    + + + +
  • Type "-"
  • +
  • Type '3'
  • +
    + + + The field should read "-3". + + +
    +
    + ); + }, +}); + +export default NumberInputs; diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt index 05ca4a1b2b..7a3dc7ffc6 100644 --- a/scripts/fiber/tests-passing.txt +++ b/scripts/fiber/tests-passing.txt @@ -837,6 +837,8 @@ src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js * should set className to empty string instead of null * should remove property properly for boolean properties * should remove property properly even with different name +* should update an empty attribute to zero +* should always assign the value attribute for non-inputs * should remove attributes for normal properties * should not remove attributes for special properties * should not leave all options selected when deleting multiple @@ -1449,6 +1451,10 @@ src/renderers/dom/shared/wrappers/__tests__/ReactDOMInput-test.js * should allow setting `value` to `objToString` * should not incur unnecessary DOM mutations * should properly control a value of number `0` +* should properly control 0.0 for a text input +* should properly control 0.0 for a number input +* should properly transition from an empty value to 0 +* should properly transition from 0 to an empty value * should have the correct target value * should not set a value for submit buttons unnecessarily * should control radio buttons @@ -1482,6 +1488,11 @@ src/renderers/dom/shared/wrappers/__tests__/ReactDOMInput-test.js * sets value properly with type coming later in props * does not raise a validation warning when it switches types * resets value of date/time input to fix bugs in iOS Safari +* always sets the attribute when values change on text inputs +* does not set the value attribute on number inputs if focused +* sets the value attribute on number inputs on blur +* an uncontrolled number input will not update the value attribute on blur +* an uncontrolled text input will not update the value attribute on blur src/renderers/dom/shared/wrappers/__tests__/ReactDOMOption-test.js * should flatten children to a string diff --git a/src/renderers/dom/fiber/wrappers/ReactDOMFiberInput.js b/src/renderers/dom/fiber/wrappers/ReactDOMFiberInput.js index f125280c5a..f0bbc0adca 100644 --- a/src/renderers/dom/fiber/wrappers/ReactDOMFiberInput.js +++ b/src/renderers/dom/fiber/wrappers/ReactDOMFiberInput.js @@ -138,11 +138,8 @@ var ReactDOMInput = { ? props.checked : props.defaultChecked, initialValue: props.value != null ? props.value : defaultValue, + controlled: isControlled(props), }; - - if (__DEV__) { - node._wrapperState.controlled = isControlled(props); - } }, updateWrapper: function(element: Element, props: Object) { @@ -195,13 +192,24 @@ var ReactDOMInput = { var value = props.value; if (value != null) { - // Cast `value` to a string to ensure the value is set correctly. While - // browsers typically do this as necessary, jsdom doesn't. - var newValue = '' + value; + if (value === 0 && node.value === '') { + node.value = '0'; + // Note: IE9 reports a number inputs as 'text', so check props instead. + } else if (props.type === 'number') { + // Simulate `input.valueAsNumber`. IE9 does not support it + var valueAsNumber = parseFloat(node.value, 10) || 0; - // To avoid side effects (such as losing text selection), only set value if changed - if (newValue !== node.value) { - node.value = newValue; + // eslint-disable-next-line + if (value != valueAsNumber) { + // Cast `value` to a string to ensure the value is set correctly. While + // browsers typically do this as necessary, jsdom doesn't. + node.value = '' + value; + } + // eslint-disable-next-line + } else if (value != node.value) { + // Cast `value` to a string to ensure the value is set correctly. While + // browsers typically do this as necessary, jsdom doesn't. + node.value = '' + value; } } else { if (props.value == null && props.defaultValue != null) { diff --git a/src/renderers/dom/shared/HTMLDOMPropertyConfig.js b/src/renderers/dom/shared/HTMLDOMPropertyConfig.js index d7d5ddc80c..ccd92eaf7c 100644 --- a/src/renderers/dom/shared/HTMLDOMPropertyConfig.js +++ b/src/renderers/dom/shared/HTMLDOMPropertyConfig.js @@ -210,6 +210,34 @@ var HTMLDOMPropertyConfig = { httpEquiv: 'http-equiv', }, DOMPropertyNames: {}, + DOMMutationMethods: { + value: function(node, value) { + if (value == null) { + return node.removeAttribute('value'); + } + + // Number inputs get special treatment due to some edge cases in + // Chrome. Let everything else assign the value attribute as normal. + // https://github.com/facebook/react/issues/7253#issuecomment-236074326 + if (node.type !== 'number' || node.hasAttribute('value') === false) { + node.setAttribute('value', '' + value); + } else if ( + node.validity && + !node.validity.badInput && + node.ownerDocument.activeElement !== node + ) { + // Don't assign an attribute if validation reports bad + // input. Chrome will clear the value. Additionally, don't + // operate on inputs that have focus, otherwise Chrome might + // strip off trailing decimal places and cause the user's + // cursor position to jump to the beginning of the input. + // + // In ReactDOMInput, we have an onBlur event that will trigger + // this function again when focus is lost. + node.setAttribute('value', '' + value); + } + }, + }, }; module.exports = HTMLDOMPropertyConfig; diff --git a/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js b/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js index 8408415424..5fcc92a825 100644 --- a/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js +++ b/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js @@ -296,6 +296,35 @@ describe('DOMPropertyOperations', () => { }); }); + describe('value mutation method', function() { + it('should update an empty attribute to zero', function() { + var stubNode = document.createElement('input'); + var stubInstance = {_debugID: 1}; + ReactDOMComponentTree.precacheNode(stubInstance, stubNode); + + stubNode.setAttribute('type', 'radio'); + + DOMPropertyOperations.setValueForProperty(stubNode, 'value', ''); + spyOn(stubNode, 'setAttribute'); + DOMPropertyOperations.setValueForProperty(stubNode, 'value', 0); + + expect(stubNode.setAttribute.calls.count()).toBe(1); + }); + + it('should always assign the value attribute for non-inputs', function() { + var stubNode = document.createElement('progress'); + var stubInstance = {_debugID: 1}; + ReactDOMComponentTree.precacheNode(stubInstance, stubNode); + + spyOn(stubNode, 'setAttribute'); + + DOMPropertyOperations.setValueForProperty(stubNode, 'value', 30); + DOMPropertyOperations.setValueForProperty(stubNode, 'value', '30'); + + expect(stubNode.setAttribute.calls.count()).toBe(2); + }); + }); + describe('deleteValueForProperty', () => { var stubNode; var stubInstance; diff --git a/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js b/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js index 4a026bcbea..1b1d5b95c0 100644 --- a/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js +++ b/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js @@ -258,6 +258,26 @@ function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) { } } +function handleControlledInputBlur(inst, node) { + // TODO: In IE, inst is occasionally null. Why? + if (inst == null) { + return; + } + + // Fiber and ReactDOM keep wrapper state in separate places + let state = inst._wrapperState || node._wrapperState; + + if (!state || !state.controlled || node.type !== 'number') { + return; + } + + // If controlled, assign the value attribute to the current value on blur + let value = '' + node.value; + if (node.getAttribute('value') !== value) { + node.setAttribute('value', value); + } +} + /** * This plugin creates an `onChange` event that normalizes change events * across form elements. This event fires at a time when it's possible to @@ -316,6 +336,11 @@ var ChangeEventPlugin = { if (handleEventFunc) { handleEventFunc(topLevelType, targetNode, targetInst); } + + // When blurring, set the value attribute for number inputs + if (topLevelType === 'topBlur') { + handleControlledInputBlur(targetInst, targetNode); + } }, }; diff --git a/src/renderers/dom/shared/wrappers/__tests__/ReactDOMInput-test.js b/src/renderers/dom/shared/wrappers/__tests__/ReactDOMInput-test.js index c16b2f578a..62df49a453 100644 --- a/src/renderers/dom/shared/wrappers/__tests__/ReactDOMInput-test.js +++ b/src/renderers/dom/shared/wrappers/__tests__/ReactDOMInput-test.js @@ -423,6 +423,48 @@ describe('ReactDOMInput', () => { expect(node.value).toBe('0'); }); + it('should properly control 0.0 for a text input', () => { + var stub = ; + stub = ReactTestUtils.renderIntoDocument(stub); + var node = ReactDOM.findDOMNode(stub); + + node.value = '0.0'; + ReactTestUtils.Simulate.change(node, {target: {value: '0.0'}}); + expect(node.value).toBe('0.0'); + }); + + it('should properly control 0.0 for a number input', () => { + var stub = ; + stub = ReactTestUtils.renderIntoDocument(stub); + var node = ReactDOM.findDOMNode(stub); + + node.value = '0.0'; + ReactTestUtils.Simulate.change(node, {target: {value: '0.0'}}); + expect(node.value).toBe('0.0'); + }); + + it('should properly transition from an empty value to 0', function() { + var container = document.createElement('div'); + + ReactDOM.render(, container); + ReactDOM.render(, container); + + var node = container.firstChild; + + expect(node.value).toBe('0'); + }); + + it('should properly transition from 0 to an empty value', function() { + var container = document.createElement('div'); + + ReactDOM.render(, container); + ReactDOM.render(, container); + + var node = container.firstChild; + + expect(node.value).toBe(''); + }); + it('should have the correct target value', () => { var handled = false; var handler = function(event) { @@ -1074,4 +1116,88 @@ describe('ReactDOMInput', () => { 'node.setAttribute("checked", "")', ]); }); + + describe('assigning the value attribute on controlled inputs', function() { + function getTestInput() { + return React.createClass({ + getInitialState: function() { + return { + value: this.props.value == null ? '' : this.props.value, + }; + }, + onChange: function(event) { + this.setState({value: event.target.value}); + }, + render: function() { + var type = this.props.type; + var value = this.state.value; + + return ; + }, + }); + } + + it('always sets the attribute when values change on text inputs', function() { + var Input = getTestInput(); + var stub = ReactTestUtils.renderIntoDocument(); + var node = ReactDOM.findDOMNode(stub); + + ReactTestUtils.Simulate.change(node, {target: {value: '2'}}); + + expect(node.getAttribute('value')).toBe('2'); + }); + + it('does not set the value attribute on number inputs if focused', () => { + var Input = getTestInput(); + var stub = ReactTestUtils.renderIntoDocument( + , + ); + var node = ReactDOM.findDOMNode(stub); + + node.focus(); + + ReactTestUtils.Simulate.change(node, {target: {value: '2'}}); + + expect(node.getAttribute('value')).toBe('1'); + }); + + it('sets the value attribute on number inputs on blur', () => { + var Input = getTestInput(); + var stub = ReactTestUtils.renderIntoDocument( + , + ); + var node = ReactDOM.findDOMNode(stub); + + ReactTestUtils.Simulate.change(node, {target: {value: '2'}}); + ReactTestUtils.SimulateNative.blur(node); + + expect(node.getAttribute('value')).toBe('2'); + }); + + it('an uncontrolled number input will not update the value attribute on blur', () => { + var stub = ReactTestUtils.renderIntoDocument( + , + ); + var node = ReactDOM.findDOMNode(stub); + + node.value = 4; + + ReactTestUtils.SimulateNative.blur(node); + + expect(node.getAttribute('value')).toBe('1'); + }); + + it('an uncontrolled text input will not update the value attribute on blur', () => { + var stub = ReactTestUtils.renderIntoDocument( + , + ); + var node = ReactDOM.findDOMNode(stub); + + node.value = 4; + + ReactTestUtils.SimulateNative.blur(node); + + expect(node.getAttribute('value')).toBe('1'); + }); + }); }); diff --git a/src/renderers/dom/stack/client/wrappers/ReactDOMInput.js b/src/renderers/dom/stack/client/wrappers/ReactDOMInput.js index f1fc6a6068..2f30e43a26 100644 --- a/src/renderers/dom/stack/client/wrappers/ReactDOMInput.js +++ b/src/renderers/dom/stack/client/wrappers/ReactDOMInput.js @@ -128,11 +128,8 @@ var ReactDOMInput = { : props.defaultChecked, initialValue: props.value != null ? props.value : defaultValue, listeners: null, + controlled: isControlled(props), }; - - if (__DEV__) { - inst._wrapperState.controlled = isControlled(props); - } }, updateWrapper: function(inst) { @@ -188,13 +185,24 @@ var ReactDOMInput = { var node = ReactDOMComponentTree.getNodeFromInstance(inst); var value = props.value; if (value != null) { - // Cast `value` to a string to ensure the value is set correctly. While - // browsers typically do this as necessary, jsdom doesn't. - var newValue = '' + value; + if (value === 0 && node.value === '') { + node.value = '0'; + // Note: IE9 reports a number inputs as 'text', so check props instead. + } else if (props.type === 'number') { + // Simulate `input.valueAsNumber`. IE9 does not support it + var valueAsNumber = parseFloat(node.value, 10) || 0; - // To avoid side effects (such as losing text selection), only set value if changed - if (newValue !== node.value) { - node.value = newValue; + // eslint-disable-next-line + if (value != valueAsNumber) { + // Cast `value` to a string to ensure the value is set correctly. While + // browsers typically do this as necessary, jsdom doesn't. + node.value = '' + value; + } + // eslint-disable-next-line + } else if (value != node.value) { + // Cast `value` to a string to ensure the value is set correctly. While + // browsers typically do this as necessary, jsdom doesn't. + node.value = '' + value; } } else { if (props.value == null && props.defaultValue != null) { From f6a64cad5dee29b50a2284e52aed353e7e7a5ee4 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Mon, 27 Mar 2017 12:48:05 -0700 Subject: [PATCH 3/7] Use radio buttons for toggle --- examples/fiber/index.html | 48 +++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/examples/fiber/index.html b/examples/fiber/index.html index a62f2bfcaf..0d8ac00029 100644 --- a/examples/fiber/index.html +++ b/examples/fiber/index.html @@ -128,10 +128,10 @@ super(); this.state = { seconds: 0, - useTimeSlicing: true + useTimeSlicing: true, }; this.tick = this.tick.bind(this); - this.toogleTimeSlicing = this.toogleTimeSlicing.bind(this); + this.onTimeSlicingChange = this.onTimeSlicingChange.bind(this); } componentDidMount() { this.intervalID = setInterval(this.tick, 1000); @@ -140,15 +140,15 @@ if (this.state.useTimeSlicing) { // Update is time-sliced. ReactDOMFiber.unstable_deferredUpdates(() => { - this.setState(state => ({ ...state, seconds: (state.seconds % 10) + 1 })); + this.setState(state => ({ seconds: (state.seconds % 10) + 1 })); }); } else { // Update is not time-sliced. Causes demo to stutter. - this.setState(state => ({ ...state, seconds: (state.seconds % 10) + 1 })); + this.setState(state => ({ seconds: (state.seconds % 10) + 1 })); } } - toogleTimeSlicing() { - this.setState(state => ({ ...state, useTimeSlicing: !state.useTimeSlicing})); + onTimeSlicingChange(value) { + this.setState(() => ({ useTimeSlicing: value })); } componentWillUnmount() { clearInterval(this.intervalID); @@ -161,7 +161,16 @@ const transform = 'scaleX(' + (scale / 2.1) + ') scaleY(0.7) translateZ(0.1px)'; return (
    - +
    +

    Time-slicing

    +

    Toggle this and observe the effect

    + +
    @@ -174,6 +183,31 @@ } } + class Toggle extends React.Component { + constructor(props) { + super(); + this.onChange = this.onChange.bind(this); + } + onChange(event) { + this.props.onChange(event.target.value === 'on'); + } + render() { + const value = this.props.value; + return ( + + ); + } + } + var start = new Date().getTime(); function update() { ReactDOMFiber.render( From fca5d3ffc5e9288a831770769949349cc8739b4f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 27 Mar 2017 14:59:37 -0700 Subject: [PATCH 4/7] Added missing @flow annotation to ReactNativeFeatureFlags file (#9267) --- src/renderers/native/ReactNativeFeatureFlags.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderers/native/ReactNativeFeatureFlags.js b/src/renderers/native/ReactNativeFeatureFlags.js index 1686eec51a..1224b86f7b 100644 --- a/src/renderers/native/ReactNativeFeatureFlags.js +++ b/src/renderers/native/ReactNativeFeatureFlags.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ReactNativeFeatureFlags + * @flow */ 'use strict'; From a749f4fb63657a3af1502047a5af876ebabb0e64 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 28 Mar 2017 11:24:13 -0700 Subject: [PATCH 5/7] Adding fix to EventPropagators that was accidentally omitted from PR #9219 (#9274) --- src/renderers/shared/shared/event/EventPropagators.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/shared/shared/event/EventPropagators.js b/src/renderers/shared/shared/event/EventPropagators.js index db9537b833..05d6863d38 100644 --- a/src/renderers/shared/shared/event/EventPropagators.js +++ b/src/renderers/shared/shared/event/EventPropagators.js @@ -93,7 +93,7 @@ function accumulateTwoPhaseDispatchesSingleSkipTarget(event) { * requiring that the `dispatchMarker` be the same as the dispatched ID. */ function accumulateDispatches(inst, ignoredDirection, event) { - if (event && event.dispatchConfig.registrationName) { + if (inst && event && event.dispatchConfig.registrationName) { var registrationName = event.dispatchConfig.registrationName; var listener = getListener(inst, registrationName); if (listener) { From aaabd655a6961dfa31ef997a020e04a5054af971 Mon Sep 17 00:00:00 2001 From: Sasha Aickin Date: Tue, 28 Mar 2017 14:55:22 -0700 Subject: [PATCH 6/7] Added jest-cli to the package.json for the Fiber record-tests script. (#9270) --- scripts/fiber/record-tests | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/fiber/record-tests b/scripts/fiber/record-tests index b7c57bf18a..99257e92f1 100755 --- a/scripts/fiber/record-tests +++ b/scripts/fiber/record-tests @@ -8,9 +8,9 @@ const fs = require('fs'); const os = require('os'); const path = require('path'); -const SearchSource = require('jest-cli').SearchSource; -const TestRunner = require('jest-cli').TestRunner; -const TestWatcher = require('jest-cli/build/TestWatcher'); +const SearchSource = require('jest').SearchSource; +const TestRunner = require('jest').TestRunner; +const TestWatcher = require('jest').TestWatcher; const createHasteContext = require('jest-runtime').createHasteContext; const readConfig = require('jest-config').readConfig; From 660306982c43410bb0aa7a275a06389319d86676 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 29 Mar 2017 13:49:30 +0100 Subject: [PATCH 7/7] added DEV condition around getDeclarationErrorAddendum contents --- src/renderers/dom/fiber/ReactDOMFiberComponent.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/renderers/dom/fiber/ReactDOMFiberComponent.js b/src/renderers/dom/fiber/ReactDOMFiberComponent.js index f52a6422cd..89df93b868 100644 --- a/src/renderers/dom/fiber/ReactDOMFiberComponent.js +++ b/src/renderers/dom/fiber/ReactDOMFiberComponent.js @@ -68,10 +68,12 @@ var { var DOC_FRAGMENT_TYPE = 11; function getDeclarationErrorAddendum() { - var ownerName = getCurrentFiberOwnerName(); - if (ownerName) { - // TODO: also report the stack. - return '\n\nThis DOM node was rendered by `' + ownerName + '`.'; + if (__DEV__) { + var ownerName = getCurrentFiberOwnerName(); + if (ownerName) { + // TODO: also report the stack. + return '\n\nThis DOM node was rendered by `' + ownerName + '`.'; + } } return ''; }