UNPKG

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