React DOM textarea
一、作用
二、验证文本区域属性
备注
getCurrentFiberOwnerNameInDevOrNull()由 ReactCurrentFiber#getCurrentFiberOwnerNameInDevOrNull 实现
/**
* Implements a <textarea> host component that allows setting `value`, and
* `defaultValue`. This differs from the traditional DOM API because value is
* usually set as PCDATA children.
*
* 实现了一个 <textarea> 宿主组件,允许设置 `value` 和 `defaultValue`。
* 这与传统的 DOM API 不同,因为 value 通常是作为 PCDATA 子节点来设置的。
*
* If `value` is not supplied (or null/undefined), user actions that affect the
* value will trigger updates to the element.
*
* 如果未提供 `value`(或为 null/undefined),影响该值的用户操作将触发元素的更新。
*
* If `value` is supplied (and not null/undefined), the rendered element will
* not trigger updates to the element. Instead, the `value` prop must change in
* order for the rendered element to be updated.
*
* 如果提供了 `value`(且不是 null 或 undefined),渲染的元素将不会触发自身的更新。
* 相反,必须更改 `value` 属性,渲染的元素才会更新。
*
* The rendered element will be initialized with an empty value, the prop
* `defaultValue` if specified, or the children content (deprecated).
*
* 渲染的元素将以空值、指定的 `defaultValue` 属性或子内容(已弃用)进行初始化。
*/
export function validateTextareaProps(element: Element, props: Object) {
if (__DEV__) {
if (
props.value !== undefined &&
props.defaultValue !== undefined &&
!didWarnValDefaultVal
) {
console.error(
'%s contains a textarea with both value and defaultValue props. ' +
'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://react.dev/link/controlled-components',
getCurrentFiberOwnerNameInDevOrNull() || 'A component',
);
didWarnValDefaultVal = true;
}
if (props.children != null && props.value == null) {
console.error(
'Use the `defaultValue` or `value` props instead of setting ' +
'children on <textarea>.',
);
}
}
}
三、更新文本区域
备注
toString()由 ToStringValue#toString 实现getToStringValue()由 ToStringValue#getToStringValue 实现
export function updateTextarea(
element: Element,
value: ?string,
defaultValue: ?string,
) {
const node: HTMLTextAreaElement = element as any;
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.
// 将 `value` 转换为字符串以确保值被正确设置。
// 虽然浏览器通常会在必要时自动处理,但 jsdom 并不会。
const newValue = toString(getToStringValue(value));
// To avoid side effects (such as losing text selection), only set value if changed
// 为避免副作用(例如失去文本选择),仅在值改变时设置
if (newValue !== node.value) {
node.value = newValue;
}
// TOOO: This should respect disableInputAttributeSyncing flag.
// TODO: 这应该遵守 disableInputAttributeSyncing 标志。
if (defaultValue == null) {
if (node.defaultValue !== newValue) {
node.defaultValue = newValue;
}
return;
}
}
if (defaultValue != null) {
node.defaultValue = toString(getToStringValue(defaultValue));
} else {
node.defaultValue = '';
}
}
四、初始化文本区域
备注
getToStringValue()由 ToStringValue#getToStringValue 实现track()由 inputValueTracking#track 实现disableTextareaChildren由 ReactFeatureFlags#disableTextareaChildren 提供
export function initTextarea(
element: Element,
value: ?string,
defaultValue: ?string,
children: ?string,
) {
const node: HTMLTextAreaElement = element as any;
let initialValue = value;
// Only bother fetching default value if we're going to use it
// 只有在需要使用默认值时才去获取它
if (initialValue == null) {
if (children != null) {
if (!disableTextareaChildren) {
if (defaultValue != null) {
throw new Error(
'If you supply `defaultValue` on a <textarea>, do not pass children.',
);
}
if (isArray(children)) {
if (children.length > 1) {
throw new Error('<textarea> can only have at most one child.');
}
children = children[0];
}
defaultValue = children;
}
}
if (defaultValue == null) {
defaultValue = '';
}
initialValue = defaultValue;
}
const stringValue = getToStringValue(initialValue);
// 这将被转为字符串。
node.defaultValue = stringValue as any; // This will be toString:ed.
// This is in postMount because we need access to the DOM node, which is not
// available until after the component has mounted.
// 这段代码放在 postMount 中,因为我们需要访问 DOM 节点,而组件挂载之前无法访问。
const textContent = node.textContent;
// Only set node.value if textContent is equal to the expected
// initial value. In IE10/IE11 there is a bug where the placeholder attribute
// will populate textContent as well.
// 仅当 textContent 等于预期的初始值时才设置 node.value。
// 在 IE10/IE11 中存在一个错误,placeholder 属性也会填充 textContent。
// https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
if (textContent === stringValue) {
if (textContent !== '' && textContent !== null) {
node.value = textContent;
}
}
track(element as any);
}
五、填充文本区域
备注
toString()由 ToStringValue#toString 实现getToStringValue()由 ToStringValue#getToStringValue 实现queueChangeEvent()由 ReactDOMEventReplaying#queueChangeEvent 实现
export function hydrateTextarea(
element: Element,
value: ?string,
defaultValue: ?string,
): void {
const node: HTMLTextAreaElement = element as any;
let initialValue = value;
if (initialValue == null) {
if (defaultValue == null) {
defaultValue = '';
}
initialValue = defaultValue;
}
// Track the value that we last observed which is the hydrated value so
// that any change event that fires will trigger onChange on the actual
// current value.
// 跟踪我们最后观察到的值,即已恢复的值,这样任何触发的更改事件都会在实际的当前值上触发 onChange。
const stringValue = toString(getToStringValue(initialValue));
const changed = trackHydrated(node as any, stringValue, false);
if (changed) {
// If the current value is different, that suggests that the user
// changed it before hydration. Queue a replay of the change event.
// 如果当前值不同,这表明用户在水化之前已经更改了它。将更改事件的重放加入队列。
queueChangeEvent(node);
}
}
六、恢复受控文本区域状态
export function restoreControlledTextareaState(
element: Element,
props: Object,
) {
// DOM component is still mounted; update
// DOM 组件仍然挂载;更新
updateTextarea(element, props.value, props.defaultValue);
}
七、变量
1. 已警告默认值
let didWarnValDefaultVal = false;