以下会涉及到的技术点:react mobx compositionstart compositionupdate compositionend
在使用 input 时,通常会对输入的内容做校验,校验的方式无非两种:
现有如下需求:“仅允许输入英文、数字和汉字,不允许输入其他特殊字符和符号”。显然这种场景需要使用第二种校验方式。
然后我自以为很机智的写了下面的代码(引入了组件库 cloud-react),在输入值变化的时候(onChange 事件),处理绑定到 input 上的 value,将除了英文、数字、和汉字之外的字符都替换成空字符串。
export default class CompositionDemo extends Component {
constructor() {
this.state = {
value: ''
};
}
onChange(evt) {
this.setState({
value: evt.target.value.replace(/[^a-zA-Z0-9\u4E00-\u9FA5]/g, '')
});
};
render() {
return <Input
onChange={this.onChange.bind(this)}
value={this.state.value}
/>
}
}
平平常常,普普通通,一切看起来都是正常的操作,结果,当我输入拼音的时候,神奇的事情发生了:连拼的时候除了最后一个字,前面的都变成了字符。
what??? 小问号,你是否有很多朋友?
于是,我踏上了一条不归路,呸呸呸,是打开了新世界的大门,就是这个门对于我来说可能有点沉,推了两天才看到新世界。
纠其原因:拼音输入是一个过程,确切的说,在这个过程中,你输入的每一个字母都触发了 onChange 事件,而你输入过程中的这个产物在校验中被吃掉了,留下了一坨空字符串,所以就发生了上面那个神奇的现象。
这里需要用到两个属性:compositionstart、compositionend
简单点来说,就是当你开始使用输入法进行新的输入的时候,会触发 compositionstart ,中间过程其实也有一个函数 compositionupdate,顾名思义,输入更新时会触发它;当结束输入法输入的时候,会触发 compositionend。
下面进入正题:
首先,我们先看一下 Input 组件的一个很正常的实现:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class InputDemo1 extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
};
}
static getDerivedStateFromProps({ value }, { value: preValue }) {
if (value !== preValue) {
return { value };
}
return null;
}
onChange = evt => {
this.props.onChange(evt);
};
render() {
return <input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
}
}
Input 组件有两种应用场景:
不受控的输入框在我使用过程中并没有什么 bug,此处不做赘述,此处只谈受控的输入框,也就是我们需求(仅允许输入英文、数字和汉字,不允许输入其他特殊字符和符号)中需要使用的场景。
前面提到的 compositionstart 和 compositionend 该出场了:利用这两个属性的特点,在输入拼音的“过程中”不让 input 触发 onChange 事件,自然就不会触发校验,好了,既然有了思路,开始码代码。
我们定义一个变量 isOnComposition 来判断是否在“过程中”
isOnComposition = false;
handleComposition = evt => {
if (evt.type === 'compositionend') {
this.isOnComposition = false;
return;
}
this.isOnComposition = true;
};
onChange = evt => {
if (!this.isOnComposition) {
this.props.onChange(evt);
}
};
render() {
const commonProps = {
onChange: this.onChange,
onCompositionStart: this.handleComposition,
onCompositionUpdate: this.handleComposition,
onCompositionEnd: this.handleComposition,
};
return <input
value={this.state.value}
type="text"
{...commonProps}
/>
}
你以为就这么轻松解决了么?
呵,你想多了!
我仍然使用开篇那个 demo 来测试这个代码,发现事情又神奇了一点呢,这次拼音压根就输不进去了哇~
我查看了下在输入拼音时函数的调用:
是的,宁没有看错,只触发了onCompositionstart 和 onCompositionupdate这两个函数,我起初以为是逻辑被我写扣圈了,想了想原因(其实我想了好久,人略笨,见笑):
罪魁祸首就是绑定在 input 上的那个 value,输入拼音的过程中,state.value 一直没变,input 中自然不会有任何输入值,没有输入值,也就完成不了输入过程,触发不了 compositionend,一直处于“过程中”。
所以这次不是程序逻辑扣圈,是中断了。
于是我又想如何把中断的程序接起来(对的,垮掉了我们就捡起来,哈),完成这个链条。
我想了好多办法,也在网上看了好多办法,可惜都解决不了我的困境。
各种心酸不堪回首,幸好最后找到了一个办法:其实想想原来代码中用 state.value 去控制 input 值的变化,还是没有把 input 中何时输入值的控制权放在自己手里,“过程中”这个概念也就失去了意义。只要 state.value 还和 input 绑在一起,就是我自己玩我自己的,人家玩人家的。于是,就有了下面让控制权回到我手中的代码。
import React, { Component, createRef } from 'react';
import PropTypes from 'prop-types';
export default class InputDemo extends Component {
inputRef = createRef();
isOnComposition = false;
componentDidMount() {
this.setInputValue();
}
componentDidUpdate() {
this.setInputValue();
}
setInputValue = () => {
this.inputRef.current.value = this.props.value || ''
};
handleComposition = evt => {
if (evt.type === 'compositionend') {
this.isOnComposition = false;
return;
}
this.isOnComposition = true;
};
onChange = evt => {
if (!this.isOnComposition) {
this.props.onChange(evt);
}
};
render() {
const commonProps = {
onChange: this.onChange,
onCompositionStart: this.handleComposition,
onCompositionUpdate: this.handleComposition,
onCompositionEnd: this.handleComposition,
};
return <input
ref={this.inputRef}
type="text"
{...commonProps}
/>
}
}
测了一下,大致上是没问题了。
还要看一下谷歌浏览器和火狐浏览器,果然还有坑:
最后再做一下兼容处理,修改一下 handleComposition 函数
handleComposition = evt => {
if (evt.type === 'compositionend') {
this.isOnComposition = false;
// 谷歌浏览器:compositionstart onChange compositionend
// 火狐浏览器:compositionstart compositionend onChange
if (navigator.userAgent.indexOf('Chrome') > -1) {
this.onChange(evt);
}
return;
}
this.isOnComposition = true;
};
因为不管中间执行了那些函数,最后都是需要执行 onChange 事件的,因此加了判断,对谷歌浏览器做了特殊处理(其它浏览器暂时没做考虑和处理)。
完整代码 https://github.com/liyuan-meng/my-react-app/tree/master/src/inputAndComposition
到此,正文结束了,我还要说两个需要注意的地方,其实也是踩了的坑:
来自:https://segmentfault.com/a/1190000022931120
JavaScript语言设计太灵活,用起来不免要多加小心掉进坑里面。如今网站几乎100%使用JavaScript。JavaScript看上去是一门十分简单的语言,然而事实并不如此。它有很多容易被弄错的细节,一不注意就导致BUG。
和你们一样,我也是一个普普通通的前端开发者,在日常工作中,大部分时间不是在写新代码,而是在改代码,或是需求被改了,或是报bug了。当别人想我们报一个bug,直到我们把bug完整的修复好,整个过程是一个怎样的经历?
苍天啊!大地啊!竟然有程序员,因为沉迷代码,快要痛失女友了!那么,程序员写起代码,究竟可以有多沉(shang)迷(yin)?程序员为了写代码,可以有多疯?看了网友们的评论
开发应用程序是一个非常有压力的工作。没有人是完美的,因此在这个行业中,代码中出现bug是相当普遍的现象。面对bug,一些程序员会生气,会沮丧,会心烦意乱,甚至会灰心丧气,而另一些程序员会依然保持冷静沉着
缺陷的定义:软件没有实现产品的说明书所描述的功能、软件实现了产品说明书描述不应有的功能...缺陷的等级-致命:一招毙命的缺陷,使你的系统无法运行,有造成数据泄漏的安全性问题。严重:可以引起易于纠正的异常情况、可能引起易于修复的故障或对产品外观难以接受的缺陷。
在学习ant design的自定义主题这一功能时候,官方给到创建config-overrides.js文件,并且写入如下代码, 查阅资料发现react-scripts 升级到 2.1.2 以后破坏了 react-app-rewired,react-app-rewired的新版本删除所有方法injectBabelPlugin
如今软件开发迭代频繁,随之而来的是产品质量难以保障,用户一天天被动找到 bug 而骂开发,开发要么被拉去祭天,要么拉慢开发新功能的进度条,分出时间精力处理 bug。这已经成了软件开发行业的一大难题,有什么解决方案呢?
当你遇到问题的时候,只能通过debug功能来确定问题,你应该考虑打日志,良好的系统,是可以通过日志进行问题定位的。当你碰到if…else 或者 switch这样的分支时,要在分支的首行打印日志,用来确定进入了哪个分支
有许多可视化的 Bug 跟踪工具,通常可以根据特性集、集成和定价进行分类。选择一个 Bug 跟踪工具时,需要整体考虑团队的规模和需求。对于很多团队来说,项目管理并不是必需的,因此对 Bug 跟踪工具的真正需求变成了一个强大的解决方案提供者
周会上同事抛出了一个问题,程序员如何减少开发中的 Bug?很有意思的一个话题,本篇文章我们来进行探讨与总结。爱因斯坦曾经说过:「如果给我一个小时解答一道决定我生死的问题,我会花55分钟来弄清楚这道题到底是在问什么
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!