从系统报错角度看 React 组件的的受控与非受控

前言

在 React 官网对受控组件与非受控组件有清晰简洁的解释(受控组件 非受控组件)。

网上关于 React 中受控组件与非受控组件的文章和博客也有很多。主要集中在是什么以及怎么用。本文的角度是在用错或用混的情形下,在开发者模式中通过React 返回的警告信息,从反向角度加深理解这两个概念。 毕竟犯错才能长记性,更让人刻骨铭心。

下面的例子中的报错信息是我最近在开发组件库时遇到过的。错误信息列表会保持更新。

input 属性中的 defaultValue 与 value 不能一起使用

这个错误是在开发 Input 输入框组件时无意中引起的。

我们想为控件提供一个 defaultValue 的 props,使用户可以设置输入框的初始值。因此组件同时提供了两个 props 即 value 与 defaultValue。于是很 ‘自然’ 而 ‘无意’ 的在defaultProps 中将 defaultValue 设置为空字符串。当用户更改输入时,组件内部将 value 绑定到一个内部 state 作为输入框值的唯一来源。

这样导致事实上 props的 value 和 defaultValue 同时出现在了 input 中。我们在组件 render 方法中使用了 ES6 的解构赋值,defaultValue 被透传至 input 组件,从代码中很难直观发现这个错误,直到运行调试报错。。
在这里插入图片描述

Warning: Input contains an input of type undefined with both value and defaultValue props. Input elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props.

export default class Input extends React.Component<InputProps, any> {
	static propTypes = {
	    // ...
        value: PropTypes.any,
        defaultValue: PropTypes.any
    };
    static defaultProps = {
    	// ...
        defaultValue: ''
    };
	
	render() {
		const {
            onChange,
            ...restProps
        } = this.props;
		return <input 
				...restProps 
				onChange={e => this.onChange(e)}
				value={this.state.value}
		>
		</input>
	}
}

这是个开发环境下的 warning ,在生产环境中不会暴露,在某些场景下也未必会引发 bug。但是开发环境中的系统警告更有助于我们真正理解什么是 React 式编程。

这个问题的解决办法也很简单,去掉 defaultProps 中的 defaultValue。当用户设置 defaultValue时将其作为 state 中 value 的初始值。

总结从系统警告中我们可以重温一下受控组件的含义, input 输入框如果作为受控组件很排斥 defaultValue 这个 html 标签属性。它的出现相当于通知 React: 我不希望把组件的控制权交给 React (即通过单向数据流 state 作为组件值的唯一来源,onChange 事件改变 state),我自己可以通过传统的 DOM 方式来操纵。

Antd 式的 Form 表单不设置 initialValue

后果如系统警告所示:组件从非受控状态到受控状态的切换不被推荐
在这里插入图片描述

Warning: A component is changing an uncontrolled input of type undefined to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

Antd Form 的关于 initialValue 的细节在这里不展开讲。以 Form 表单中的 input 控件为例,我们只要知道,如果不设置 initialValue,相当于 input value 的初始值为 undefined. 类似下面的代码:

import React from 'react';

export default class MyForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {} // 初始 state 没有定义 value
    }

    render() {
        return (
            <form className="add-support-staff-form">
                <input name="name" type="text" value={this.state.name} onChange={this.onFieldChange('name').bind(this)}/>
            </form>
        )
    }

    onFieldChange(fieldName) {
        return function (event) {
            this.setState({[fieldName]: event.target.value});
        }
    }
}

export default MyForm;

对于 input 受控组件,应该是 value 绑定 state,通过 setState 来改变 value。
而上述情况中,input 的初始 value 值为 undefined,并没有绑定任何 state,因此在初始情况下 input 为非受控组件。change 事件中 state.name 被动态设定并与 input value 属性绑定,此时 input 符合受控组件的定义。所以 input 经历了一个从非受控到受控状态的转变,不符合 React 规范,因此系统发出警告信息。

结语

通过上面的例子我们再回头来看 React 官方文档中关于受控与非受控组件的定义,是不是会有更深的体会:

受控组件:

在 HTML 中,表单元素(如、 和 )之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。

我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

非受控组件:

要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值