一、受控组件
相当于数据的双向绑定,理解为vue中的v-model。将input的值存入state中。
- HTML中的表单元素是可输入的,也就是有自己的可变状态
- 而React中可变状态通常保存在state中,并且只能通过
setState()
方法来修改 - React将state与表单元素值value绑定在一起,有state的值来控制表单元素的值
- 受控组件:值受到react控制的表单元素
使用步骤
- 在state中添加一个状态,作为表单元素的value值
- 给表单元素绑定change事件,将表单元素的值设置为state的值
示例demo
class App extends React.Component {
constructor(){
super()
this.inputChange = this.inputChange.bind(this)
//bind使inputChange里的this 指向class类的this
}
state = {
txt : ''
}
inputChange(e){
this.setState({
txt: e.target.value
})
}
render(){
console.log(this.state);
return (
<div>
{/* 把state的值设置给输入框的value,绑定change事件,这样用户在输入内容的时候调用相应函数,在函数里面把当前设置的值赋值给state,从而达到数据的统一 */}
<input type="text" value={this.state.txt} onChange={this.inputChange}/>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById('root'))
多表单元素示例
多表单元素优化
- 问题:每个表单元素都有一个单独的事件处理函数,这样太繁琐
- 优化:使用一个事件处理程序同时处理多个表单元素
步骤
- 给表单元素添加name属性(用来区分是哪一个表单),名称与state相同(用来更新数据的)
- 根据表单内容来获取对应值
- 在change事件处理程序中通过 [name] 来修改对应的state
inputChange(e){
let target = e.target;
let value = target.type == 'checkbox' ? target.checked : target.value;
this.setState({
[e.target.name]: value
})
}
<input type="text" value={this.state.txt} name="txt" onChange={this.inputChange}/>
<input type="checkbox" value={this.state.isChecked} name="isChecked" onChange={this.inputChange}/>
二、非受控组件 (了解)
- 说明:借助于ref,使用元素DOM方式获取表单元素值
- ref的作用:获取DOM或者组件
使用步骤
- 调用
React.createRef()
方法创建ref对象 - 将创建好的 ref 对象添加到文本框中
- 通过ref对象获取到文本框的值
class App extends React.Component {
constructor(){
super()
//创建 ref
this.txtRef = React.createRef()
}
// 获取文本框的值
getTxt =() => {
console.log(this.txtRef.current.value)
}
render(){
return (
<div>
<input type ="text" ref={this.txtRef} />
<button onClick ={this.getTxt}>获取值</button>
</div>
)
}
}
三、React组件综合案例
需求分析
- 渲染评论列表(列表渲染)
- 没有评论数据时渲染:暂无评论(条件渲染)
- 获取评论信息,包括评论人和评论内容(受控组件)
- 发表评论,更新评论列表(
setState()
)
class App extends React.Component {
state = {
userName: '',
userContent: '',
comments: [
{ id: 1, name: 'jack', content: '沙发!!!' },
{ id: 2, name: 'rose', content: '板凳~' },
{ id: 3, name: 'tom', content: '楼主好人' }
]
}
// 渲染暂无评论
renderList() {
return this.state.comments.length > 0 ? (
<ul> {
this.state.comments.map(item => {
return (
<li key={item.id}>
<h3>{item.name}</h3>
<p>{item.content}</p>
</li>
)
})
}
</ul>)
: (<div className="no-comment">暂无评论,快去评论吧~</div>)
}
// 双向绑定表单
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
})
}
// 绑定点击事件
handleClick = e => {
//拿到用户输入的内容
let { userName, userContent } = this.state
if (userName.trim === '' || userContent.trim() === '') {
alert('请输入内容')
return
}
// 利用数组拓展运算符来进行数据的拼接,把用户输入的存放在数组的第一个位置
let newComments = [
{
id: this.state.comments.length + 1,
name: userName,
content: userContent
}, ...this.state.comments
]
this.setState(
{
comments: newComments,
userName: '',
userContent: ''
}
)
}
render() {
return (
<div className='box'>
<input type="text" placeholder='请输入评论人' className='user' name="userName" value={this.state.userName} onChange={this.handleChange}></input>
<textarea className='content' name="userContent" value={this.state.userContent} onChange={this.handleChange}></textarea>
<button onClick={this.handleClick}>发表评论</button>
<div style={{ "height": "200px", "width": "100%", 'overflow': 'scroll' }}>
{this.renderList()}
</div>
</div>
)
}
}
createRoot(document.getElementById('root')).render(<App></App>)
index.css
.box {
display: flex;
flex-direction: column;
justify-content: space-between;
box-sizing: border-box;
padding: 10px;
width: 300px;
height: 500px;
border: 1px solid grey;
margin: 0 auto;
}
.user{
width: 100%;
}
.content{
width: 100%;
height: 40%;
}
四、表单处理
获取用户所填写的内容我们必须要监听表单onChange事件,在表单项发生变化时获取其中的内容,在响应函数中通过事件对象的target.value来获取用户填写的内容
const nameChangeHandler= e => {
//e.target.value 表示当前用户输入的值
};
然后我们再将该函数设置为input元素的onChange事件的响应函数:
<div>
用户名 <input type="text" onChange={nameChangeHandler}/>
</div>
这样一来当用户输入内容时,nameChangeHandler就会被触发,从而通过e.target.value来获取用户输入的值。通常我们还会为表单项创建一个state用来存储值:
const [inputName, setInputName] = useState('');
const nameChangeHandler = e => {
//e.target.value 表示当前用户输入的值
setInputName(e.target.value);
};
上例中用户名存储到了变量inputName中,inputName也会设置为对应表单项的value属性值,这样一来当inputName发生变化时,表单项中的内容也会随之改变:
<div>
用户名 <input type="text" onChange={nameChangeHandler} value={inputName}/>
</div>
当用户输入内容后会触发onChange事件从而调用nameChangeHandler函数,在函数内部调用了setInputName设置了用户输入的用户名。
在表单中输入内容会影响到state的值,同时当我们修改state的值时,由于表单项的value属性值指向了state,表单也会随state值一起改变。这种绑定方式我们称为双向绑定,即表单会改变state,state也可以改变表单,在开发中使用双向绑定的表单项是最佳实践。
那么表单的提交要如何处理呢?表单提交同样需要通过事件来处理,提交表单的事件通过form标签的onSubmit事件来绑定,处理表单的方式因情况而已,但是一定要注意,必须要取消默认行为,否则会触发表单的默认提交行为:
const formSubmitHandler = e => {
e.preventDefault();
/*
* username : inputName
* password : pwdInput
* email : inputEmail
* */
};
为表单绑定事件:
<form onSubmit={formSubmitHandler}>
......
</form>
如此我们便有了一个简单的表单案例,完整代码如下:
在这个案例中,表单的所有功能包括输入、显示、提交全部都由React所处理。这种表单项在React中被称为受控组件,即表单项受React控制。当然也存在有不受控组件,但那种组件使用机会少且需要通过原生DOM去操作表单,并不建议使用,所以这里便不再赘述了。