函数式组件中的属性(props)

  • 函数式组件中的props(属性)与类组件还是有点不同的,因为函数式组件没有this指向,但是却可以有形参传入,因此props属性就是以形参传入的方式传递给子组件的,且函数式组件设置属性默认值和属性验证的方式与类组件的第二种方法非常的相似,都是以函数式组件名.defaultProps设置默认属性和以函数式组件名.propTypes设置属性校验,如下所示:

代码展示

  • 父组件
import React, { Component } from 'react'
import Naviber from './Naviber'//类组件
import List from './List'//函数式组件
export default class App extends Component {
render() {
// 使用props(属性)传值(父组件通过key=value的形式传值给子组件)类似于Vue的props
return (
<div>
<div>
<h2>首页</h2>
{/* 将title属性和isShowBtn传给子组件 */}
<Naviber title="home" isShowBtn={false}/>
</div>

<div>
<h2>列表</h2>
{/* 不传isShowBtn属性过去给子组件(函数式组件) */}
<List title="list" />
</div>

<div>
<h2>购物车</h2>
{/* 不传isShowBtn属性过去给子组件(类组件) */}
<Naviber title="cart"/>
</div>
</div>
)
}
}
  • 子组件(函数式)
import React from 'react'

// 引入属性验证库(propTypes,引入名字可以随便取)
import myPropTypes from 'prop-types'

export default function List(props) {
console.log('函数式组件接收到的props属性',props);
let {title,isShowBtn} = props// 子组件接收父组件传过来的props属性
return (
<div>
{isShowBtn && <button>返回</button>}
<ul>
<li>列表数据-{title}</li>
<li>列表数据-{title}</li>
<li>列表数据-{title}</li>
</ul>
{<button>返回{title}</button>}
</div>
)
}

// 函数式组件设置默认属性
List.defaultProps = {
isShowBtn:true
}

// 函数式组件设置属性验证
List.propTypes = {
title:myPropTypes.string,
isShowBtn:myPropTypes.bool
}

状态VS属性

  • 相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
  • 不同点:
    1. 属性能从父组件获取,状态不能
    2. 属性可以由父组件修改,状态不能
    3. 属性能在内部设置默认值,状态也可以,设置方式不一样
    4. 属性不在组件内部修改,状态要在组件内部修改
    5. 属性能设置子组件初始值,状态不可以
    6. 属性可以修改子组件的值,状态不可以

详细说明:

  • state的主要作用是用于组件保存、控制、修改自己的可变状态。state在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为state是一个局部的、只能被组件自身控制的数据源。state中状态可以通过this.setState()方法更新,且每一次状态更新(setState调用都会使得组件重新渲染)

  • props的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的props,否则组件的props永远保持不变。

  • 没有state的组件叫无状态组件(stateless component),设置了state的组件叫做有状态组件(stateful component),因为状态会带来管理的复杂性,因此我们更加推崇多地写无状态组件,少写有状态组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。

表单中的受控组件非受控组件

什么是受控组件非受控组件

  • 可以广义的认为:页面中所有输入类的DOM如果是现用现取得称为非受控组件,而通过setState将输入的值维护到state中,需要时再从state获取,数据就受到了state得控制,这样的就是受控组件。或者React组件的数据渲染是否被调用者传递的 props 完全控制,控制则为受控组件,否则非受控组件

受控组件

在使用表单来收集用户输入时,例如<input><select><textarea>等元素都要绑定一个change事件,当表单状态发生变化时,就会触发onChange事件,更新组件的state。这种组件为受控组件,在受控组件中,组件渲染出的状态与它的valuechecked属性相对应,react通过这种方法消息组件的局部状态,是整个状态可控。

React中定义了一个input输入框的话,它并没有类似于Vuev-model的这种双向绑定功能,也就是说我们并没有一个指令能够将数据和输入框结合起来,用户在输入框中输入内容,然后数据同步更新。

import React, { Component } from 'react'

export default class App extends Component {
state = {
inputValue : '',
}
render() {
return (
<div>
<input value={this.state.inputValue}/>
<button>登录</button>
<button>重置</button>
</div>
)
}
}

用户在界面上的输入框输入内容时,它是自己维护了一个state,这个state并不是我们平常看见的this.state,而是每个表单元素上抽象的state,这样的话就能根据用户的输入自己进行UI上的更新,如果我们想要控制输入框的内容,而输入框的内容取决的是input中的value属性,那么我们可以在this.state中定义一个名为inputValue的属性,并将input上的value指定为这个属性。

但是这时候你会发现input的内容是只读的,因为value会被我们的this.state.inputValue所控制,当用户输入新的内容时,this.state.inputValue并不会自动更新,这样的话input内的内容也就不会变了,此时控制台通常会抛出一个Warning
image

您为表单字段提供了一个没有onChange处理程序的value属性,这将呈现只读字段,如果字段应该是可变的,请使用defaultValue,否则请设置onChangereadOnly

这段Warning其实给出了对于这个问题的解决方案,我们只需要对组件的onChange事件来监听输入内容的改变并使用setState更新this.state.username即可,如此我们在当前组件中能够控制这个表单元素的值,这就是受控组件。

import React, { Component } from 'react'

export default class App extends Component {
state = {
inputValue : '',//设置输入框状态
}
render() {
return (
<div>
{/* 类似于Vue中的双向数据绑定,但是react中是单向数据流 */}
<input value={this.state.inputValue}
onChange={e => {
// input框的每一次输出都会调用setState重新渲染页面更新state中的数据
this.setState({inputValue: e.target.value})
// 因为this.setState为异步操作,因此延时10ms再输出结果,否者输出的是前一个结果
setTimeout(()=>{
console.log(this.state.inputValue);
},10)
}}
/>
<button onClick={() => console.log('当前用户为:',this.state.inputValue)}>登录</button>
<button onClick={() => this.setState({inputValue: ''})}>重置</button>
</div>
)
}
}

  • 结果展示:
    image

但是这种受控组件因为是存在弊端的,尽管此时Input组件本身是一个受控组件,但与之相对的调用方(父级组件)失去了更改Input组件值的控制权,所以对调用方(父级组件)而言,Input组件是一个非受控组件,也就是说这个受控组件无法实现数据的传输,不仅如此,表单元素的值都是由当前的React组件(state)进行管理,当有多个输入框,或者多个这种组件时,如果想同时获取到全部的值就必须每个都编写事件处理函数,这会让代码看起来很臃肿,所以为了解决这些情况,出现了非受控组件。

非受控组件

如果表单元素并不经过state或没有valuechecked属性时,而是通过ref修改或者直接操作DOM,那么它的数据无法通过state控制,这就是非受控组件。

可以认为,非受控组件就是具有属性不受当前组件控制的组件

import React, { Component } from 'react'

export default class App extends Component {
inputValue = React.createRef();//设置ref属性
render() {
return (
<div>
{/* 非受控组件没有value值或者checked值,数据由调用者于组件间流通,组件不保管数据 */}
<input defaultValue={'lam'} ref={this.inputValue}/>
{/* 通过ref属性来获取dom中的数据 */}
<button onClick={() => console.log('当前用户为:',this.inputValue.current.value)}>登录</button>
<button onClick={() => this.inputValue.current.value=''}>重置</button>
</div>
)
}
}
  • 结果展示:
    image

总结

受控组件

  • 每当表单的状态发生变化时,都会被写入到组件的state中。
  • 在受控组件中,组件渲染出的状态与它的valuechecked 相对应。
  • react受控组件更新state的流程:
    • 通过在初始state中设置表单的默认值。
    • 每当表单的值发生变化时,调用onChange事件处理器。
    • 事件处理器通过合成对象event拿到改变后的状态,并更新应用的state
    • setState触发视图的重新渲染,完成表单组件值的更新。

非受控组件

  • 如果一个表单组件没有value或者checked就可以称为非受控组件。

  • 非受控组件是一种反模式,它的值不受组件自身的stateprops控制。

  • 通常需要为其添加ref属性来访问渲染后的底层DOM元素。

  • 可通过添加defaultValue指定默认value值。