一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天, 点击查看活动详情 。
大家好,我是前端西瓜哥。
今天我们用 React 来实现一个只能输入数字的 input。
先写一个基础的 input 受控组件。
function Form() {
const [numVal, setNumVal] = useState('');
return (
<input type="text" value={numVal} onChange={e => setNumVal(e.target.value)} />
上面这种写法没有做任何限制,你可以在 input 元素上输入任何内容。
只能输入数字字符
现在我们要加入限制:只能输入数字字符。
很容易想到的做法是在 setNumVal 上封装多一层方法。这个方法会将 e.target.value 中非数字字符的部分移除,然后再传入到 setNumVal
function Form() {
const [numVal, setNumVal] = useState('');
const handleChange = (val: string) => {
val = val.replace(/[^\d]/g, '');
setNumVal(val);
return (
<input type="text" value={numVal} onChange={e => handleChange(e.target.value)} />
这里用了 string.prototype.replace 方法,/[^\d]/
表示找非数字字符,g 表示多次查找,如果不带上,replace 只会替换第一个匹配字符串。
找到它们并将它们替换为空字符串,也就是移除掉,最后返回一个只有数字字符的字符串。
为了可以在任何的 input 或输入框组件上使用,我们不妨将其封装为 hook。
const useNumStrState = (defaultVal = ''): [string, (val: string) => void] => {
const [numVal, setNumVal] = useState(defaultVal);
const handleChange = (val: string) => {
val = val.replace(/[^\d]/g, '');
setNumVal(val);
return [numVal, handleChange];
// 使用
function Form() {
const [numVal, setNumVal] = useNumStrState('');
return (
<input type="text" value={numVal} onChange={e => setNumVal(e.target.value)} />
这样,一个有着基础能力的只能输入数字的 input 就完成了。
增强:移除前导 0 版本
下面再实现一个去掉前导 0 的版本,让用户只能输入合法的整数。
const useNumStrState = (defaultVal = ''): [string, (val: string) => void] => {
const removeLeadingZeros = (s: string) => {
const oldLen = s.length;
s = s.replace(/^0+/, ''); // 移除前导零
// 全为 0 的情况,留一个 0
if (s.length === 0 && oldLen > 0) {
s = '0';
return s;
defaultVal = removeLeadingZeros(defaultVal);
const [numVal, setNumVal] = useState(defaultVal);
const handleChange = (val: string) => {
val = val.replace(/[^\d]/g, '');
val = removeLeadingZeros(val);
setNumVal(val);
return [numVal, handleChange];
同样也是用 replace 方法,/^0+/
的意思是匹配从字符串首位开始的连续尽可能多的 0,然后将它们替换为为空字符串。
这里有一种特殊的情况,就是全是 0 的字符串,比如 0000,这时候需要保留一个 0。
让 input 只能输入数字并不复杂,只需要在 onChange 的时候将 e.target.value 进行处理,得到只含数字的字符串,再用它来修改状态就好了。
另外也不得不说 React Hook 是真的牛逼,让我们可以做到比组件更细粒度的复用逻辑封装。
文章首发公众号:前端西瓜哥