课程名称
课程章节
第3章 React基础精讲
- 3-1 使用React编写TodoList功能
- 3-2 React 中的响应式设计思想和事件绑定
课程讲师
课程内容
使用React编写TodoList功能
删除App.js
新建一个TodoList组件
import React from 'react';
import ReactDOM from 'react-dom/client';
import TodoList from './TodoList';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<TodoList />
</React.StrictMode>
);
import React, { Component } from "react";
class TodoList extends Component {
render(){
return (
<div>TodoList</div>
)
}
}
export default TodoList;
import React, { Component } from "react";
class TodoList extends Component {
render(){
return (
<div>
<input></input>
<button>提交</button>
</div>
)
}
}
export default TodoList;
需求:
● 添加任务
● 删除任务
实现:往input框里输入内容,点击提交可以将其显示到下方列表中。
添加列表
import React, { Component } from "react";
class TodoList extends Component {
render(){
return (
<div>
<input></input>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>Learning Math</li>
</ul>
)
}
}
export default TodoList;
报错:jsx元素出现了问题,jsx中要求一个组件,render函数的返回值,外层必须只有一个包裹元素,但是这里最外层里有两个元素,这就违反了规定。在最外层加一个div。
import React, { Component } from "react";
class TodoList extends Component {
render(){
return (
<div>
<div>
<input></input>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>Learning Math</li>
</ul>
</div>
)
}
}
export default TodoList;
flex布局中,就希望顶层有多个标签,不要在外层再多加一个标签,而这又与jsx的语法矛盾。
想在最外层包裹一个元素,又同时希望该元素不显示出来,React16提供了一个新语法 => Fragment占位符
import React, { Component, Fragment } from "react";
class TodoList extends Component {
render(){
return (
<Fragment>
<div>
<input></input>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>Learning Math</li>
</ul>
</Fragment>
)
}
}
export default TodoList;
课程收获
掌握React 中的响应式设计思想和事件绑定 =>
实现:在input框里输入内容,点击提交,会将其加入到列表中 =>
思路:在提交按钮上绑定一个事件,获取到input框里的value值,再找到列表的dom节点,将input框里的value值挂载到该dom节点中。
这是直接操作dom的形式,React的设计思想和以前直接操作dom的思想是完全不同的。
React是一个响应式的框架,它在做编程的时候,强调不要直接操作dom,操作的是数据,通过数据的变化,React会自动的感知到数据的变化,自动帮助我们生成dom。因此在写React代码的时候,再也不用关注dom相关的操作了,只需要关注数据层的操作。
实际在做这样一个简单功能的时候,页面上会有多少数据呢?其实只需要两组数据就够了,一组数据存储input框里的值,一组数据存储列表中的每一项。
在代码中定义两个数据项 =>
TodoList这个组件,即是一个类,在js中一个类就一定有一个构造函数 => constructor
当创建一个TodoList实例,或者使用该组件的时候,constructor是优于任何函数(该类的方法),最先被执行的函数。
constructor固定可以接收一个参数 => props
super(props) => TodoList组件是继承了React.Component组件,所以需要在创建TodoList组件的时候,super实际指的就是父类,即React.Component组件,调用React.Component的(父类的)构造函数。
在React中写构造函数注意两点:
1、接收props参数
2、super(props) => 调用父级的构造函数
import React, { Component, Fragment } from "react";
class TodoList extends Component {
constructor(props){
super(props);
}
render(){
return (
<Fragment>
<div>
<input></input>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>Learning Math</li>
</ul>
</Fragment>
)
}
}
export default TodoList;
定义数据 =>
React中需要把数据定义在状态里 => this.state
● 称为“组件的状态”
● 在其中可以存很多很多的东西
○ inputValue 存储input框里的值
○ list 存列表里的每一项数据,设置为数组类型
如何绑定input的value值与inputValue,进行关联 => value = this.state.inputValue,input的value值由inputValue决定,在jsx的与法中,this.state.inputValue实际是js的一个变量,如果想在jsx语法中使用js的表达式或者变量,它的语法是在最外层加一个大括号“{}”。
设置inputValue默认值,可以看到input框里的值由它决定 =>
import React, { Component, Fragment } from "react";
class TodoList extends Component {
constructor(props){
super(props);
this.state = {
inputValue: "hello!!!!!",
list: []
}
}
render(){
return (
<Fragment>
<div>
<input value={this.state.inputValue}></input>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>Learning Math</li>
</ul>
</Fragment>
)
}
}
export default TodoList;
React会自动感知到数据的变化,会将数据映射到页面上,页面会自动响应数据的变化。
我们尝试在input框里输入内容,但是input框里的值始终不变化,因为input的值是由this.state.inputValue 决定的,而且这里this.state.inputValue的值,固定写死了。所以input框里永远是这个值,不管在input框里输入任何内容,显然这样控制是不对的。
在input框上绑定事件,在React种也可以进行事件绑定,如果想监听input框数据发生改变。React的事件绑定和原生的事件绑定有一个差别,原生是onchange,而React是onChange,“C”要大写,这是React事件绑定的语法。注意绑定事件处理函数的时候,也需要加大括号。
onChange={this.handleInputChange} => handleInputChange中打印一下事件对象
import React, { Component, Fragment } from "react";
class TodoList extends Component {
constructor(props){
super(props);
this.state = {
inputValue: "hello!!!!!",
list: []
}
}
render(){
return (
<Fragment>
<div>
<input
value={this.state.inputValue}
onChange={this.handleInputChange}
></input>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>Learning Math</li>
</ul>
</Fragment>
)
}
handleInputChange(e){
console.log(e)
}
}
export default TodoList;
Target属性,打印一下
handleInputChange(e){
console.log(e.target)
}
e.target其实就是对应事件的dom节点,它其实有一个value值,其实就可以获取到用户在input框里输入的内容了。
想要输入什么,页面跟着变化,则必须使数据发生变化,改变state种的input的value值。
handleInputChange(e){
this.state.inputValue = e.target.value;;
}
发现输入一个内容,报错了。
state未被定义,这里肯定是this指向出了问题。
打印this指向,发现是undefined
handleInputChange(e){
console.log(this)
}
我们希望的this指向的是,TodoList组件,获取它的state状态。
=> 绑定事件处理函数的时候通过bind函数,改变this。
import React, { Component, Fragment } from "react";
class TodoList extends Component {
constructor(props){
super(props);
this.state = {
inputValue: "hello!!!!!",
list: []
}
}
render(){
return (
<Fragment>
<div>
<input
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)}
></input>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>Learning Math</li>
</ul>
</Fragment>
)
}
handleInputChange(e){
console.log(this)
// this.state.inputValue = e.target.value;;
}
}
export default TodoList;
这就可以,但是改变input值还是没效果。
handleInputChange(e){
this.state.inputValue = e.target.value;;
}
原因:在React中,想改变state的状态,不能直接通过this.state的引用这种方式去改,React给每一个组件提供了一个方法,this.setState() 去改变state的数据。
handleInputChange(e){
this.setState({
inputValue: e.target.value
})
}
一开始,render函数渲染的时候,会把input的value值,渲染成inputValue的值。当在input输入内容的时候,input的onChange事件就会触发,执行handleInputChange方法,获取到用户输入的内容,并将其赋值给inputValue。inputValue值发生了改变,页面就会自动跟着变化了。
总结:1、state负责存储组件中的数据;2、jsx中想用js的表达式或者变量,需要用花括号包起来;3、事件绑定时候主要this指向问题,可以使用bind函数变更;4、改变state值,不能直接改,需要通过setState函数去改,通过传递对象的方式更改。