input value tracking
一、作用
二、轨道
export function track(node: ElementWithValueTracker) {
if (getTracker(node)) {
return;
}
const valueField = isCheckable(node) ? 'checked' : 'value';
// This is read from the DOM so always safe to coerce. We really shouldn't
// be coercing to a string at all. It's just historical.
// 这是从 DOM 中读取的,所以强制转换总是安全的。我们其实不应该根本就进行字符串强制
// 转换。这只是历史遗留问题。
const initialValue = '' + (node[valueField] as any);
node._valueTracker = trackValueOnNode(node, valueField, initialValue);
}
三、追踪水合
export function trackHydrated(
node: ElementWithValueTracker,
initialValue: string,
initialChecked: boolean,
): boolean {
// For hydration, the initial value is not the current value but the value
// that we last observed which is what the initial server render was.
// 对于 hydration,初始值不是当前值,而是我们最后观察到的值,也就是初始服务端渲染
// 的值。
if (getTracker(node)) {
return false;
}
let valueField: 'checked' | 'value';
let expectedValue;
if (isCheckable(node)) {
valueField = 'checked';
expectedValue = '' + (initialChecked as any);
} else {
valueField = 'value';
expectedValue = initialValue;
}
const currentValue = '' + (node[valueField] as any);
node._valueTracker = trackValueOnNode(node, valueField, expectedValue);
return currentValue !== expectedValue;
}
四、如果已更改则更新值
export function updateValueIfChanged(node: ElementWithValueTracker): boolean {
if (!node) {
return false;
}
const tracker = getTracker(node);
// if there is no tracker at this point it's unlikely
// that trying again will succeed
// 如果此时没有追踪器,再尝试也不太可能成功
if (!tracker) {
return true;
}
const lastValue = tracker.getValue();
const nextValue = getValueFromNode(node);
if (nextValue !== lastValue) {
tracker.setValue(nextValue);
return true;
}
return false;
}
五、停止追踪
export function stopTracking(node: ElementWithValueTracker) {
const tracker = getTracker(node);
if (tracker) {
tracker.stopTracking();
}
}
六、工具
1. 可检查
function isCheckable(elem: HTMLInputElement) {
const type = elem.type;
const nodeName = elem.nodeName;
return (
nodeName &&
nodeName.toLowerCase() === 'input' &&
(type === 'checkbox' || type === 'radio')
);
}
2. 获取追踪器
function getTracker(node: ElementWithValueTracker) {
return node._valueTracker;
}
3. 分离追踪器
function detachTracker(node: ElementWithValueTracker) {
node._valueTracker = null;
}
4. getValueFromNode
function getValueFromNode(node: HTMLInputElement): string {
let value = '';
if (!node) {
return value;
}
if (isCheckable(node)) {
value = node.checked ? 'true' : 'false';
} else {
value = node.value;
}
return value;
}
5. 跟踪节点上的值
备注
checkFormFieldValueStringCoercion()由 CheckStringCoercion#checkFormFieldValueStringCoercion 实现
function trackValueOnNode(
node: any,
valueField: 'checked' | 'value',
currentValue: string,
): ?ValueTracker {
const descriptor = Object.getOwnPropertyDescriptor(
node.constructor.prototype,
valueField,
);
// if someone has already defined a value or Safari, then bail
// and don't track value will cause over reporting of changes,
// but it's better then a hard failure
// (needed for certain tests that spyOn input values and Safari)
// 如果某人已经定义了一个值或 Safari,那么就退出。并且不跟踪值会导致变更过度
// 报告,但这总比程序直接崩溃要好(这是某些检测输入值和 Safari 的测试所必需的)
if (
node.hasOwnProperty(valueField) ||
typeof descriptor === 'undefined' ||
typeof descriptor.get !== 'function' ||
typeof descriptor.set !== 'function'
) {
return;
}
const { get, set } = descriptor;
Object.defineProperty(node, valueField, {
configurable: true,
get: function () {
return get.call(this);
},
set: function (value) {
if (__DEV__) {
checkFormFieldValueStringCoercion(value);
}
currentValue = '' + value;
set.call(this, value);
},
});
// We could've passed this the first time
// but it triggers a bug in IE11 and Edge 14/15.
// Calling defineProperty() again should be equivalent.
// 我们本可以第一次就通过这个。但它会在 IE11 和 Edge 14/15 中触发一个 bug。
// 再次调用 defineProperty() 应该是等效的。
// https://github.com/facebook/react/issues/11768
Object.defineProperty(node, valueField, {
enumerable: descriptor.enumerable,
});
const tracker = {
getValue() {
return currentValue;
},
setValue(value: string) {
if (__DEV__) {
checkFormFieldValueStringCoercion(value);
}
currentValue = '' + value;
},
stopTracking() {
detachTracker(node);
delete node[valueField];
},
};
return tracker;
}
七、类型
1. 价值追踪器
// 价值追踪器
type ValueTracker = {
getValue(): string;
setValue(value: string): void;
stopTracking(): void;
};
// 带值跟踪器的元素
interface ElementWithValueTracker extends HTMLInputElement {
_valueTracker?: ?ValueTracker;
}