如何在反应中创建一个只允许使用大写字母的受控表单?

时间:2017-12-08 06:42:33

标签: javascript reactjs

很抱歉,经过长时间的解释,问题就在最后。

在reactjs网站(https://reactjs.org/docs/forms.html)上有一个示例,如下所示:

handleChange(event) {
  this.setState({value: event.target.value.toUpperCase()});
}

这些教程通常表示在按下某个键时以受控形式(例如在输入字段中),然后调用onChange,调用调用setState的handleChange事件,并重新呈现该组件,该组件将显示改变了的价值。但这不是全部真相。当本机DOM输入字段调用onChange时,输入字段已经'#34;呈现"通过浏览器,React不会重新渲染它。实际上,它将(重新渲染的)虚拟DOM元素的状态与本机DOM元素的状态进行比较,然后它发现没有差异,因此它什么都不做。在这种情况下,操作没有副作用(除了更改组件的状态)。

我知道对于初学者来说,在本教程的开头,一次讲述整个故事可能会让人感到困惑。但是这个" toUpperCase()"本教程中的示例具有误导性,因为它表明通过在handleChange中将值更改为value.toUpperCase()将使组件仅接受带有字母的字母,没有副作用。但事实并非如此,任何人都可以尝试这样做:只需在输入字段的中间选择一部分文本,然后按一个键。它肯定会转换为大写,但在这种情况下,React将重新设置输入的value属性,使文本光标(插入点)移动到文本的末尾。这是一个副作用,可能非常分散。例如,如果用户想要替换文本中间的单词,那么他就无法以任何实际的方式进行替换。他会说这是错的,它不起作用 - 他会说得对。在这种特殊情况下,我知道我可以设置text-transform:大写并将小写版本存储在商店中,但这也是错误的:如果我需要它是大写的,那么它当然应该是大写的在商店。视图的行为不得确定存储中的数据表示形式。视图应该只是视图,而不是其他任何内容。

我检查了许多与React相关的教程,并且所有这些教程都有#34;缺陷"。例如,这是来自reactjs.org的图表,显示了Flux的作用:

enter image description here

它表示在操作通过调度程序,存储区,然后是控制器视图后,视图将呈现。但实际情况是,View不仅会发出动作。它还会在发出动作之前改变其外观。 React希望将DOM元素视为发出动作的视图,但实际上它们会做其他事情。

使用select元素在reactjs教程中出现了同样的错误。 (https://reactjs.org/docs/forms.html)他们只是通过更改" multiple = {true}"属性,你可以有一个多选。不,你不能,它只是不起作用。当您在多选中选择多个项目时,浏览器将多次调用onChange事件:对所选的每个项目单独调用。因此,将组件的状态设置为所选项的集合(至少不是来自事件处理程序)是不可能的。我可以设置一个计时器,收集从onChange发出的所有更改,最后用收集的项调用setState()。但这违反了React的规则,因为应该有一个单一的事实来源。在这种情况下,将有多个来源(至少有一段时间,直到收集所有事件)。任何人都可以看到在具有许多异步处理程序的复杂应用程序中,这最终会导致难以调试的错误。

一个非常简单的事情("只接受输入框中的大写字母")对我来说似乎很难正确完成。要么我必须在JavaScript中实现我自己的输入框,要么为输入字段编写自定义渲染方法,以便在值更改时将光标位置保持在正确的位置。两者似乎都是坏主意。

在谷歌搜索后,我注意到有大量组件重新实现原生元素。为React编写了数百个select和multiselect组件,我怀疑这部分是因为本机DOM元素的行为方式。 (例如,他们不是纯粹的观点。)

问题:有没有人使用本机DOM组件做出反应?如果是这样,你如何实现真正的"控制"没有副作用的行为?可能吗?或者我应该不关心,并选择专门为React编写的某种UI工具包? (但即使有了这些,实现"大写输入字段"可能需要额外的编程。)

5 个答案:

答案 0 :(得分:3)

这应该足够了:

handleChange(event) {
  const input = event.target;
  const start = input.selectionStart;
  const end = input.selectionEnd;

  this.setState(
    {value: input.value.toUpperCase()},
    () => input.setSelectionRange(start, end)
  );
}

同时检查:http://blog.vishalon.net/javascript-getting-and-setting-caret-position-in-textarea

答案 1 :(得分:1)

你说的是对的,但问题不是来自React,它来自浏览器本身。

考虑以下示例:



function handler() {
  const elt = document.getElementById('test');
  elt.value = elt.value.toUpperCase();
};

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<input id="test" type="text" value="default value" onKeyPress="handler();" />
</body>
</html>
&#13;
&#13;
&#13;

在谈论React之前,你怎么能让它工作? React无法覆盖浏览器行为。

如果你想在保持光标位置的同时大写所有内容,你可以查看使用草稿js的富文本编辑。

答案 2 :(得分:1)

简单:提交后使用CSS +规范化(toUpperCase()

text-transform: uppercase;

答案 3 :(得分:1)

我编写了简单的toInputUppercase函数,该函数重写e.target.value并将属性onInput应用于要大写的<input />元素。

import React, { useState } from "react";
import ReactDOM from "react-dom";

const toInputUppercase = e => {
  e.target.value = ("" + e.target.value).toUpperCase();
};

const App = () => {
  const [name, setName] = useState("");

  return (
    <input
      name={name}
      onChange={e => setName(e.target.value)}
      onInput={toInputUppercase} // apply on input which do you want to be capitalize
    />
  );
};

ReactDOM.render(<App />, document.getElementById("root"));

希望它对您有用。

答案 4 :(得分:0)

您只需将此属性添加到输入字段中即可:

onInput={(e) => e.target.value = ("" + e.target.value).toUpperCase()}

希望它能起作用:)