UNPKG

6.34 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2013-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9'use strict';
10
11var _prodInvariant = require('./reactProdInvariant'),
12 _assign = require('object-assign');
13
14var LinkedValueUtils = require('./LinkedValueUtils');
15var ReactDOMComponentTree = require('./ReactDOMComponentTree');
16var ReactUpdates = require('./ReactUpdates');
17
18var invariant = require('fbjs/lib/invariant');
19var warning = require('fbjs/lib/warning');
20
21var didWarnValueLink = false;
22var didWarnValDefaultVal = false;
23
24function forceUpdateIfMounted() {
25 if (this._rootNodeID) {
26 // DOM component is still mounted; update
27 ReactDOMTextarea.updateWrapper(this);
28 }
29}
30
31/**
32 * Implements a <textarea> host component that allows setting `value`, and
33 * `defaultValue`. This differs from the traditional DOM API because value is
34 * usually set as PCDATA children.
35 *
36 * If `value` is not supplied (or null/undefined), user actions that affect the
37 * value will trigger updates to the element.
38 *
39 * If `value` is supplied (and not null/undefined), the rendered element will
40 * not trigger updates to the element. Instead, the `value` prop must change in
41 * order for the rendered element to be updated.
42 *
43 * The rendered element will be initialized with an empty value, the prop
44 * `defaultValue` if specified, or the children content (deprecated).
45 */
46var ReactDOMTextarea = {
47 getHostProps: function (inst, props) {
48 !(props.dangerouslySetInnerHTML == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, '`dangerouslySetInnerHTML` does not make sense on <textarea>.') : _prodInvariant('91') : void 0;
49
50 // Always set children to the same thing. In IE9, the selection range will
51 // get reset if `textContent` is mutated. We could add a check in setTextContent
52 // to only set the value if/when the value differs from the node value (which would
53 // completely solve this IE9 bug), but Sebastian+Ben seemed to like this solution.
54 // The value can be a boolean or object so that's why it's forced to be a string.
55 var hostProps = _assign({}, props, {
56 value: undefined,
57 defaultValue: undefined,
58 children: '' + inst._wrapperState.initialValue,
59 onChange: inst._wrapperState.onChange
60 });
61
62 return hostProps;
63 },
64
65 mountWrapper: function (inst, props) {
66 if (process.env.NODE_ENV !== 'production') {
67 LinkedValueUtils.checkPropTypes('textarea', props, inst._currentElement._owner);
68 if (props.valueLink !== undefined && !didWarnValueLink) {
69 process.env.NODE_ENV !== 'production' ? warning(false, '`valueLink` prop on `textarea` is deprecated; set `value` and `onChange` instead.') : void 0;
70 didWarnValueLink = true;
71 }
72 if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValDefaultVal) {
73 process.env.NODE_ENV !== 'production' ? warning(false, 'Textarea elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled textarea ' + 'and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components') : void 0;
74 didWarnValDefaultVal = true;
75 }
76 }
77
78 var value = LinkedValueUtils.getValue(props);
79 var initialValue = value;
80
81 // Only bother fetching default value if we're going to use it
82 if (value == null) {
83 var defaultValue = props.defaultValue;
84 // TODO (yungsters): Remove support for children content in <textarea>.
85 var children = props.children;
86 if (children != null) {
87 if (process.env.NODE_ENV !== 'production') {
88 process.env.NODE_ENV !== 'production' ? warning(false, 'Use the `defaultValue` or `value` props instead of setting ' + 'children on <textarea>.') : void 0;
89 }
90 !(defaultValue == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'If you supply `defaultValue` on a <textarea>, do not pass children.') : _prodInvariant('92') : void 0;
91 if (Array.isArray(children)) {
92 !(children.length <= 1) ? process.env.NODE_ENV !== 'production' ? invariant(false, '<textarea> can only have at most one child.') : _prodInvariant('93') : void 0;
93 children = children[0];
94 }
95
96 defaultValue = '' + children;
97 }
98 if (defaultValue == null) {
99 defaultValue = '';
100 }
101 initialValue = defaultValue;
102 }
103
104 inst._wrapperState = {
105 initialValue: '' + initialValue,
106 listeners: null,
107 onChange: _handleChange.bind(inst)
108 };
109 },
110
111 updateWrapper: function (inst) {
112 var props = inst._currentElement.props;
113
114 var node = ReactDOMComponentTree.getNodeFromInstance(inst);
115 var value = LinkedValueUtils.getValue(props);
116 if (value != null) {
117 // Cast `value` to a string to ensure the value is set correctly. While
118 // browsers typically do this as necessary, jsdom doesn't.
119 var newValue = '' + value;
120
121 // To avoid side effects (such as losing text selection), only set value if changed
122 if (newValue !== node.value) {
123 node.value = newValue;
124 }
125 if (props.defaultValue == null) {
126 node.defaultValue = newValue;
127 }
128 }
129 if (props.defaultValue != null) {
130 node.defaultValue = props.defaultValue;
131 }
132 },
133
134 postMountWrapper: function (inst) {
135 // This is in postMount because we need access to the DOM node, which is not
136 // available until after the component has mounted.
137 var node = ReactDOMComponentTree.getNodeFromInstance(inst);
138 var textContent = node.textContent;
139
140 // Only set node.value if textContent is equal to the expected
141 // initial value. In IE10/IE11 there is a bug where the placeholder attribute
142 // will populate textContent as well.
143 // https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
144 if (textContent === inst._wrapperState.initialValue) {
145 node.value = textContent;
146 }
147 }
148};
149
150function _handleChange(event) {
151 var props = this._currentElement.props;
152 var returnValue = LinkedValueUtils.executeOnChange(props, event);
153 ReactUpdates.asap(forceUpdateIfMounted, this);
154 return returnValue;
155}
156
157module.exports = ReactDOMTextarea;
\No newline at end of file