受控组件和非受控组件
受控组件、非受控组件都是子组件向父组件传递数据,父组件处理数据。
受控组件
某个组件渲染出的状态受state控制,可消除组件的局部状态,使整个状态可控,这种组件称为受控组件。
- 需要子组件的数据时直接从state中获取。
受控组件的流程
- 初始时,通过state中设置子组件表单的默认值。子组件如
<input><select><textearea>,value、checked属性受控制。 - 子组件绑定一个change事件,当表单的状态发生变化,就会触发onChange事件,通过e.detail得到value的值,实时更新父组件的state。
- 子组件根据新的state重新渲染,实时更新value属性的值,实现双向数据流。
state = {
xx: xxx
}
<input
type=''
value={this.state.xx}
onChange={e => this.setState({xx: e.target.value})}
/>
<select value={abc} onChange={value => this.setState({abc: value})}>
<option value='' selected=''>xxx选项</option>
<option value=''>xxx选项</option>
</select>
-
表单输入的value不直接作为表单的value值,state与表单元素的value绑定在一起,由state控制表单元素的值。
input中,input框实时输入的值event.target.value设置为input的value。
select中,被选择option的value值设置为select的value,select与该option匹配。
-
可以通过受控组件如
value={}实时收集子组件的数据,无验证;也可以通过form对象的方法如
validateFields()收集最终子组件的数据,并验证。
受控组件缺点
表单元素的值都是由React组件进行管理,多个受控组件都需要编写事件处理函数,代码臃肿,所以出现了非受控组件。
控制多个表单元素
使用一个事件同时处理多个表单元素。
state = {
xx: xxx,
yy: yyy
}
fun = (e) => {
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value
this.setState({
[name]: value
})
}
<input
type=''
name='xx'
value={this.state.xx}
onChange={e => this.setState({xx: e.target.value})}或onChange={this.fun}
/>
- 给表单元素添加name属性,名称与状态/数据state中的属性名相同。
- setState()更新state,state更新表单元素的值。
非受控组件
父组件通过ref对象获取DOM元素即子组件,父组件可获取子组件中的数据,单向数据流,无验证。
- 需要子组件的数据时需要获取子组件,再取出子组件中的数据。现用现取。
以下是使用ref的第三种方法获取DOM元素,也可以使用ref前两种方法获取DOM元素。
父组件:
constructor () {
super ()
this.state = {
xx: xxx
}
this.myRef = React.createRef()
}
fun = () => {
const xxx = this.myRef.current.fn() //调用子组件中函数,获得返回值。
const xxx = this.myRef.current.value //获取子组件表单的value值。
const xxx = this.myRef.current.state.yy //获取子组件中state的值,不常用。
}
render () {
return (
<input type='' ref={this.myRef} />
)
}
子组件:
<input />
-
父组件constructor函数中创建ref对象:
this.myRef = React.createRef() -
子组件添加ref对象到表单属性,自动将标签对象Xxx添加为xx对象的current属性:
<Xxx ref={this.myRef} />若父组件向子组件传递数据:
<Xxx ref={this.myRef} imgs={imgs}/> -
父组件中this.myRef.current得到子组件,非受控组件通过ref对象获取子组件的数据。
父组件调用子组件中函数获取子组件中数据:
this.myRef.current.fn(),最好是调用它的某个方法去取值,通过方法暴露子组件的数据给父组件。
实际项目中
使用Card、Form组件收集表单数据并验证,Form组件中的非受控组件收集表单数据无验证。
-
Form.create()()包装该组件,组件可使用
getFieldDecorator()()收集数据并实时验证,使用validateFields()验证并获取数据发送请求更新数据。第一个参数为标识符;配置对象包含默认值、验证规则。第二个参数为组件标签。
验证价格时,配置对象中的验证规则包含required、自定义函数。
验证商品分类时,即Cascader标签时,form对象的
getFieldDecorator()()方法中配置对象的默认值为一个数组,可能为空、一个、两个元素,分别代表添加时、修改时的一级列表、修改时的二级列表 -
受控组件Cascader级联列表。antd组件中的Cascader标签中属性为options={}、loadData={}
options={}是要展示的数据,options可以实时变化,比如从一维数组到二维数组;loadData={}表示选中某项时的回调函数
选中某项时触发loadData的回调函数。判断选中项是一级列表还是二级列表。若选中项为一级列表则初始化一级分类options并修正叶子节点;若选中项为二级列表则异步获取该二级列表数组,并遍历数组元素生成二级分类options,并设置为当前一级列表option的children。
getCategorys是async函数,返回一个promise对象,含有状态和值。一级列表则初始化列表,为二级列表则返回该列表数组。根据该promise对象的值判断是一级列表还是二级列表。页面初始化时调用getCategorys函数。
initOptions()初始化列表。一级列表叶子节点还没确定最终结果,二级列表还没初始化。修改时需要提前初始化二级列表。
-
非受控组件PicturesWall,内部包含antd组件中的Upload组件、Modal组件。
Upload标签,属性值为请求路径action=""、请求参数名name=""、可以接收的类型accept=""、要展示的文件列表fileList={}、onChange={}、大图预览onPreview={}
Modal标签,属性值为visible={}、onCancel={}、footer={null}无确认取消按钮。Modal标签内部包含img标签,Modal标签的作用是大图预览。
子组件是非受控组件,父组件通过ref对象获取子组件中的数据。