资讯专栏INFORMATION COLUMN

[ 一起学React系列 -- 5 ] 如何优雅得使用表单控件

Charlie_Jade / 2170人阅读

摘要:假如我们从后台拉取一个数据要填入输入框,那么必须得使用受控组件,因为非受控组件只能被用户输入。不影响正常输入填充该输入框的默认值,此时不显示内容。

网页中使用的form表单大家肯定都再熟悉不过了,它主要作用是用来收集和提交信息。
React中的表单组件与我们普通的Html中的表单及其表现形式没有什么不同,所以如何使用表单我觉得再拿出来说可能是画蛇添足、毫无意义。不过再怎么样也不能辜负大家对标题的期待吧,本篇内容笔者将以控件为主体进行记录。

那么问题来了,何为控件?
在React中,将form组件(
)下的所有子组件统称为控件,比如常用的input:text, Select, input:radio等等。

对于一个表单来说,

只是这个表单的载体,它具体能做哪些事还是由其内部的控件决定的。所以如何将这些控件有效得组织起来形成一个表单体系就显得尤为重要。同时React也将控件分为受控组件(Controlled Components)和非受控组件(uncontrolled components)。不过有一点需要明确下这个分类依据并不是以组件的类别来分类而是以使用方式来分类的,具体咱们下面接着说。

受控组件

官方对受控组件的定义是:

An input form element whose value is controlled by React in this way is called a “controlled component”

简而言之就是用户输入型的表单控件是由React体系来管理。比如一个输入框,我们可以通过其value对其赋值进而实现改变输入框中的值的目的(当然不只限于输入框,还有下拉框、文本域等)。而这也是受控组件可以被React控制的关键所在。先举个例子:

import React, {Component} from "react";

class ControlledInput extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputValue: "React"
        }
    }

    render() {
        return (
            
        )
    }
}
export default ControlledInput;

通过例子我们能看出在这个组件的State中我们定义了inputValue这个字段并且将它以value的形式赋给了一个输入框,此时这个组件的表现如下:

不过拥有雪亮眼睛的朋友一眼就能看出来两个"问题":

此时的输入框不管怎么敲击键盘,输入框中的值都是不变的。熟悉原生输入框的朋友都知道,一旦它的value属性被赋了值那么相当于输入框的内容被写死了,如果想改变输入框的内容那就得改变value属性的值

输入框的value属性的值是组件State中的inputValue字段。

根据上面两个"问题"我们可以预见到:如果通过setState方法去改变inputValue的值,这样React会重新渲染组件节点中的inputValue值,不就相当于改变了输入框的value属性值了么?
那就动手实践下,我们在这个组件中添加一个按钮用它来改变inputValue值:

import React, {Component} from "react";

class ControlledInput extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputValue: "React"
        }
    }

    changeHandler = () => {
        this.setState({
            inputValue: "React Form"
        })
    };

    render() {
        return (
            
) } } export default ControlledInput;

效果与我们所遇见的一样。不过实际开发中我们可不能通过点击按钮去改变输入框的值,因为输入框需要用户的实际输入,所以我们需要在输入框DOM节点上绑定onChange实践,将用户实际输入的值set给组件的State。修改方式如下:

import React, {Component} from "react";

class ControlledInput extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputValue: "React"
        }
    }

    inputHandler = (e) => {
        const content = e.target.value;
        this.setState({
            inputValue: content
        })
    };

    render() {
        return (
            
) } } export default ControlledInput;

每当输入框中的内容发生变动我们就将变动后的内容通过State重新渲染给输入框的value属性。说到这里,可能就有人认为这不是脱裤子放屁吗?为什么要在用户输入完后再绕一圈?且不急,后面会有具体说明。我们继续往下说!!!
上面的代码就是将输入框作为一个受控组件在使用。具体流程我们可以画一个图,这样更为直观也更容易理解。

这就是受控组件的实现方式流程图。这里仅仅是用输入框作为演示主体,其实还有下拉框、文本域这类可以产生更多交互的Html标签(或者说是可以通过value属性改变其外在表现的Html标签)。不过这里笔者想特地排除下单选框和复选框,因为它们的状态太过单一。

非受控组件

说到非受控组件我相信大家都能意会它的是什么意思,就是它的状态由自己维护,实际上这也是Html标签原来的样子。比如输入框被输入了什么值,那这个值就被输入框自己管理,不由外来因素管理。或许这么做会显得更为简单,笔者一直也这么认为(仅限一般情况下)。
这里我们继续以输入框为主要演示对象继续往下说。对于一个输入框如果我们想获取它的输入内容,一般有有两个方法,一个是绑定事件在其输入期间同步的获取输入值,还有一个办法是通过原生DOM对象的value获取输入值。不过对于非受控组件我们应该最大限度得保证其纯净,如果给其绑定一个事件来获取输入值那么相当于用受控组件的实现方式去处理它会显得很冗余,所以通过原生DOM去获取输入值是最Nice的办法当然React也提供了访问原生DOM的方法: React.createRef()
这是React提供的新的访问原生DOM的方法,墙裂建议这样使用;
同样我们也使用它改变一下原来的代码:

import React, {Component} from "react";

class ControlledInput extends Component {
    constructor(props) {
        super(props);
        this.input = React.createRef();
    }

    inputHandler = () => {
        const originalDom = this.input.current;//current代表原生DOM对象
        const content = originalDom.value;
        alert(content);
    };

    render() {
        return (
            
) } } export default ControlledInput;

运行结果很完美!!!非受控组件的实现及使用方法就是这么简单,我们只管取值就行了,其它由它们自己管理就好了。

受控组件与非受控组件的不同及启示

通过上面的介绍我们了解了受控组件与非受控组件及其实现、使用方式。它们的不同之处笔者这里用一句话总结下:

受控组件的value值受React管理;非受控组件的value值由自身管理;

那这句话能给我们什么样的启示?
首先看非受控组件的value值由自身管理,对于非受控组件我们好像除了拿值其它的什么都干不了,实际情况的却如此,pass!!
再看受控组件的value值受React管理,前面我们知道受控组件对于用户输入的值需要从State中绕一下再到组件本身。那么我们可不可以在这个“绕”的过程中做点什么?比如在setState之前对用户输入的内容做点手脚什么的...带着这个想法,我们往下看!

如何选择受控组件与非受控组件

既然React将form控件分为受控组件与非受控组件自然有它自己的道理,那么我们实际开发中应该如何优雅得选择它们?假如我们只是单纯得获取用户的输入,那我们会本能的选择非受控组件,因为它使用很方便不需要写那么多复杂的逻辑;假如我们想对用户的输入做点手脚,比如字母转大小写等等...因为原生input标签不具备此功能,或许我们就得使用受控组件。当然一位外国友人对如何选择受控组件与非受控组件发表了自己的看法,笔者这里做个搬运。不想看或者没法看的朋友可以看下面的搬运内容,跳过废话直接上结论:

一次性数据获取可以任意使用其中一种。不过笔者觉得选择非受控组件会更为便捷,不需要写一大堆逻辑

提交时进行验证可以任意使用其中一种。这个仁者见仁智者见智,不过笔者还是觉得非受控组件会更好

实时验证使用受控组件。因为我们在用户输入过程中对输入值进行实时验证,而非受控组件不支持这么做,初非你用第三方库;

有条件地禁用提交按钮使用受控组件。禁用提交按钮的前提是输入值不合法,参考第三条。

对输入值进行格式化使用受控组件。非受控组件输入什么就是什么,我们无法去改变;而受控组件我们可以在获取到输入值的时候对输入值进行转换,比如大小写获取货币转换等等,最后render回控件中。

对一条数据尽心多次输入使用受控组件。同样的非受控组件的状态由自身管理,而对于受控组件我们可以对输入值做更多的处理

动态输入使用受控组件。假如我们从后台拉取一个数据要填入输入框,那么必须得使用受控组件,因为非受控组件只能被用户输入。

心得:在表单中使用单选框和复选框

为什么多带带将单选框和复选框拉出来军训?是因为它们的状态单一不需要用户输入且它们的value属性并不和自身表现有直接关系。例如我们无法通过对单选框的value设置为true从而让它变为选中的状态。而且单选框和复选框基本都是成群结队出现的,所以从受控组件与非受控组件角度来处理它们显然不适合。这里笔者推荐两种处理方式:

事件代理

比如这样写:

import React, {Component} from "react";

class ControlledInput extends Component {

    constructor(props) {
        super(props);
        this.parent = React.createRef();
    }

    componentDidMount() {
        this.parent.current.addEventListener("change", (event) => {
            //do something
            console.log(event);
        })
    }

    componentWillUnmount() {
        this.parent.current.removeEventListener("change");

    }

    render() {
        return (
            
) } } export default ControlledInput;

在一群单选框或者复选框外层包一个div并且绑定事件监听。当我们点击它们的时候就会获取到对于的事件对象,然后就可以做你想做!

动态生产

所谓动态生产就是用一个function生成所需要的一群单选框或者复选框,同时给它们绑定相关事件,比如:

import React, {Component} from "react";

class ControlledInput extends Component {

    constructor(props) {
        super(props);
        this.checkBoxs = ["A", "B", "C"];
    }

    //Process checkbox group
    createCheckBoxes = () => this.checkBoxs.map(checkbox => (
        
    ));

    checkBoxChange = (e) => {
        //do something
        console.log(e);
    };

    render() {
        return (
            
Checkboxes: {this.createCheckBoxes()}
) } } export default ControlledInput;

对于这两种方法,大家仁者见仁智者见智。不过如果有其他的方法欢迎留言 :)

写在最后

其实也没啥写的,就想再提一下输入框的三个属性帮助不熟悉的朋友多了解下。对于Html输入框很熟悉的朋友此处可以跳过了。

placeholder: 主要用来提示用户这个输入框需要输入什么样的内容。不影响正常输入!

defaultValue: 填充该输入框的默认值,此时不显示placeholder内容。不影响正常输入!

value: 起到填充输入框内容的作用;与defaultValue不能共存;且一旦设置了value属性(即使为空或者无值)那么该输入框将无法进行输入。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108510.html

相关文章

  • [ 一起React系列 -- 8 ] React中的文件上传

    摘要:前言本期的主题是在中如何实现文件上传。文件上传解决方案目前比较主流的解决方案就是表单或者和表单来实现。文件上传解决方案表单利用表单组件进行文件上传是远古时期就一直在用的方法而且还真经久不衰,厉害了。 终于抽出时间来继续更新自己的博客,最近忙得够呛...对于该系列博客不知道大家有没有这样的看法,对于React常见的基础东西并没有过多或者详细列出,感觉有点不符合系列标题。的确,笔者一开始也...

    Travis 评论0 收藏0
  • [ 一起React系列 -- 0 ] React技术栈习路线

    摘要:的出现真可谓是前端界的福音,正与之宗旨所说,。据统计,目前世界上有的项目使用了。技术栈学习路线直到前段时间笔者的朋友给推荐了一个,真是欣喜若狂也更加坚定了自己在继续前进的想法。这是一个外国友人总结的一套技术栈学习路线,先给传送门。 我相信点进来的同学都是冲着标题来的,当然本文也不会让各位失望。不过在正式介绍标题所述的内容之前,我们不妨先放下技术,一起回顾下自己做前端技术的心路历程。 前...

    Java3y 评论0 收藏0
  • [ 一起React系列 -- 9 ] React中的文件下载

    摘要:本篇所说的文件下载也是基于和或者都行。的返回值是一个有意思的对象,它包含了很多方法,其中一个方法就是。通过的响应头获取到文件名。接下来就是对标签的一系列操作,然后模拟点击事件触发下载动作。 距离上次博文更新已经快一个月了,期间忙于各种事情无法脱身。今天难得闲暇 and then 就来更新啦...上篇中我们了解了下载React中如何实现文件的上传,虽然不算什么高大上的技术但实际开发的时候...

    Jacendfeng 评论0 收藏0
  • [ 一起React系列 -- 6 ] 秘术之时间旅行-1

    摘要:所谓的时间旅行从广义上来说无非就是三个动作回到过去进入未来回到现在,这个无论是从现实还是前端技术来说都是可靠的。单从技术栈来说,时间旅行不是一门技术而是一个思想套路。 标题看起来挺新颖的,笔者都觉得很高大上是不是哈哈... 抛转 时间旅行在生活中是一个非常吸引人的概念,虽然现在无法实现但说不定未来的某天就实现了!然后就穿梭会过去杀掉小时候的自己然后就开始懵逼自己是谁类似的狗血剧情......

    付伦 评论0 收藏0
  • [ 一起React系列 -- 4 ] 透传的Context

    摘要:官方对的介绍是意思就是提供了一种通过组件树传递数据的方法,而无需在每个级别手动传递。这也是基于重要物证哈哈实例使用学习技术最终是要有产出的。依然被视作一个组件,不过不同的是它的子组件必须是一个方法并且该方法接收当前对象并最终返回一个节点。 抛转引玉 通过上一篇的科普我们知道如果父节点需要向子节点传递数据,那么就得通过Props来实现;那么摆在我们眼前的就有一个问题了:现有N个节点并且它...

    firim 评论0 收藏0

发表评论

0条评论

Charlie_Jade

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<