reactjs - 如何将 HTML 输入值流与 UP 或 DOWN 键流以及输入类型流结合起来?
问题描述
我有一个复杂的 React 组件,其功能类似于 HTML5 input
。它应该允许以下内容:
- 允许输入部分数字,例如
5.
、5.0
或0.
。5.0
是部分的,因为我想允许两位小数。 - 验证号码后,通过 将其发送给父母
this.props
。 - 支持多种类型,如
int
,percent
,float
, 通过一些动作切换。每种类型都有自己的验证。 - 允许特殊的键盘操作,例如
UP
增加或DOWN
减少值。
我可以设置一个结合了类型流和输入流的可观察流:
const rawInput = fromEvent<React.FormEvent<HTMLInputElement>>(this.input, "input").pipe(
map(v => v.currentTarget.value)
);
this.typeSubject.pipe(
switchMap(t => this.parseRawValue(rawInput, t))
).subscribe(
v => {
this.props.setValue(v);
this.setState({ isValid: true });
});
我不知道如何组合键盘流:
const pressedKey = fromEvent<React.KeyboardEvent<HTMLInputElement>>(this.input, "keydown").pipe(
map(k => k.keyCode),
filter(k => this.inputKeyCodes.indexOf(k) > -1)
);
鉴于下面较大的代码示例,我如何组合这三个流以保留键入和验证功能?
我尝试了combineAll
、combineLatest
、merge
、mergeAll
和的组合withLatestFrom
,但如果不在下面添加大量代码,就无法创建正确的流parseRawValue
。
当前组件在这里定义:TransformedImagesElement/src/components/editor/inputs/NumberInput.tsx
这是带有注释的较大代码示例以提供帮助。主要问题在componentDidMount
:
import * as React from 'react';
import { fromEvent, Observable, Subject, BehaviorSubject } from "rxjs";
import { map, filter, switchMap } from 'rxjs/operators';
enum NumberInputExampleType {
int = "###",
percent = "%"
}
interface INumberInputExampleProps {
type: NumberInputExampleType;
value: number | null;
setValue(value: number): void;
max: number;
min: number;
}
interface INumberInputExampleState {
type?: NumberInputExampleType | null;
rawValue: string;
isValid: boolean;
}
export class NumberInputExample extends React.PureComponent<INumberInputExampleProps, INumberInputExampleState> {
state: INumberInputExampleState = {
type: null,
rawValue: "",
isValid: true,
}
private inputKeyCodes = [
38, // UP key
40 // DOWN key
];
// Reference to the input
private input: HTMLInputElement | null;
// Get type from the props, if the state is not yet set, or from state
private get type() {
return this.state.type || this.props.type;
}
// Subject for the type
private typeSubject: Subject<NumberInputExampleType> = new BehaviorSubject(this.type);
// This is called upon some action in the rendered elements
private switchType(newType: NumberInputExampleType): void {
this.typeSubject.next(newType);
this.setState({
type: newType,
rawValue: ""
});
}
private parseValue(): string {
// Return the raw value if it is not valid
if (this.state.rawValue !== "") {
return this.state.rawValue;
}
else if (this.props.value !== null) {
return this.getValue(this.props.value);
}
return "";
}
private getValue(value: number): string {
switch (this.type) {
case NumberInputExampleType.int:
//return(value * this.props.max).toString() // Return the internal value, a float, converted to the presentation value, a string, using this.props.max
case NumberInputExampleType.percent:
// Similar logic to the above, except converting to a valid percentage
}
}
componentDidMount() {
if (this.input) {
const rawInput = fromEvent<React.FormEvent<HTMLInputElement>>(this.input, "input").pipe(
map(v => v.currentTarget.value)
);
const pressedKey = fromEvent<React.KeyboardEvent<HTMLInputElement>>(this.input, "keydown").pipe(
map(k => k.keyCode),
filter(k => this.inputKeyCodes.indexOf(k) > -1)
);
// If pressedKey is UP, increment the value. If it is DOWN, decrement the value.
// How to combine rawInput and pressedKey while keeping the functionality of this.parseRawValue?
//
this.typeSubject.pipe(
switchMap(t => this.parseRawValue(rawInput, t))
).subscribe(
v => {
this.props.setValue(v);
this.setState({ isValid: true });
});
}
}
private parseRawValue(rawInput: Observable<string>, type: NumberInputExampleType): Observable<number> {
switch (type) {
case NumberInputExampleType.int:
return rawInput.pipe(
//filter(v => ... ), // Filter invalid input, allowing only numbers or numbers-while-typing, such as '5.'
//map(v => ... ), // Ensure the "number" is between this.props.max and this.props.min
map(this.storeValueInState),
//map(v / this.props.max) // Finally, convert the represented value to the internal value, a float
)
case NumberInputExampleType.percent:
// Similar logic to the above, except converting to a valid percentage. Filter out partial numbers
}
}
private storeValueInState(value: string): string {
this.setState({
rawValue: value,
isValid: false
});
return value;
};
render() {
return <input ref={e => this.input = e} value={this.parseValue()} />;
}
}
我希望当我输入时,应该会发生以下情况,例如NumberInputExampleType.percent
:
action | rawValue | state.isValid | this.props.value
-------+----------+---------------+-----------------
init | | true | null
type 1 | 1 | true | 1
type . | 1. | false | 1
type 0 | 1.0 | false | 1
type w | 1.0 | false | 1
type 1 | 1.01 | true | 1.01
key UP | 1.02 | true | 1.02
erase | | true | null
type 9 | 9 | true | 9
type 9 | 99 | true | 99
type . | 99. | false | 99
type 9 | 99.9 | false | 99
type 9 | 99.99 | true | 99.99
key UP | 100 | true | 100
key UP | 100 | true | 100
解决方案
推荐阅读
- ajax - 如何使用ajax修复rest调用中添加的参数?
- alexa-skills-kit - alexa sdk:在 https 响应中保存 sessionAttributes
- image - 如何使用 AWS S3 和 Vapor 3 和 Leaf 显示图像
- javascript - 用户可以看到文章结尾时的Jquery阅读位置进度
- python - django large FileField 初始化和内存
- python - 如何按绝对值对数组编号进行排序
- python - 如何获取文本的 CSS 选择器
- sqlite - 在子查询的 group by 中使用外部查询的字段
- bash - Bash:增加 Java 属性文件中的版本号(代号一版本)
- mongodb - 如何对数组内的 JSONS 进行正则表达式查询