知识点
1、React编写TodoList功能
2、React响应式设计思想和事件绑定
3、实现TodoList新增删除功能
4、JSX语法补充
5、拆分组件与组件间传值
6、TodoList代码优化
7、react衍生思考
index.js(注释两行,增加两行)
...
// import App from './App';
import TodoList from './TodoList';
...
// ReactDOM.render( , document.getElementById('root'));
ReactDOM.render(<TodoList />, document.getElementById('root'));
TodoList.js(新增js文件)
import React, { Component, Fragment } from 'react';
import logo from './img/logo.svg';
import './css/TodoList.css';
//render return 只能返回一个整体,不能返回多个同级节点
// 占位符,不显示
class TodoList extends Component{
render(){
return (
<Fragment>
<div><input type="text" /><button>提交</button></div>
<ul>
<li>学习英语</li>
<li>learning react</li>
</ul>
</Fragment>
);
}
}
export default TodoList;
TodoList.js(修改js文件)
import React, { Component, Fragment } from 'react';
import './css/TodoList.css';
//render return 只能返回一个整体,不能返回多个同级节点
// 占位符,不显示
class TodoList extends Component{
//最优先执行函数
constructor(props) {
super(props);//调用父类的构造函数
//定义数据
//组件的初始化状态
this.state={
inputValue:'hello',
list:[]
};
}
render(){
return (
<Fragment>
<div>
<input type="text"
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)}
/>
<button>提交</button>
</div>
<ul>
<li>学习英语</li>
<li>learning react</li>
</ul>
</Fragment>
);
}
handleInputChange(e){
// console.log(e);//返回事件对象
// console.log(e.target);//返回input的dom节点
// console.log(e.target.value);//返回input的dom节点的value
// this.state.inputValue=e.target.value;
//1、会报错,因为this指向错误,给onChange事件修改this指向,使其指向当前组件TodoList
//2、报错Use setState(),不能直接赋值,需要使用setState方法
this.setState({
inputValue:e.target.value
});
}
}
export default TodoList;
button
绑定一个点击事件,该点击事件主要修改state数据,不要关注dom层面修改,而要关注修改数据层面。list:[...this.state.list, this.state.inputValue]
中的...this.state.list
表示展开list
数组,与inputValue
形成一个新数组。提交后,清空输入框的内容,将inputValue
置空。ul
列表数据,使用map()
方法对list
进行循环。React坑:采用写法一会报警告【index.js:1437 Warning: Each child in a list should have a unique “key” prop.】,写法二中key={index}
将index
作为key
值是一个不好的习惯,但目前因为每个index
都不同,暂时将index
作为key
值。<ul>
{
this.state.list.map((item,index) => {
return <li>{item}</li> //写法一,报警告
return <li key={index}>{item}</li> //写法二,不报警告
})
}
</ul>
绑定一个删除item的点击事件handleItemDelete
,不要直接修改state的数据。TodoList.js(修改js文件)
import React, { Component, Fragment } from 'react';
import './css/TodoList.css';
//render return 只能返回一个整体,不能返回多个同级节点
// 占位符,不显示
class TodoList extends Component{
//最优先执行函数
constructor(props) {
super(props);//调用父类的构造函数
//定义数据
//组件的初始化状态
this.state={
inputValue:'hello',
list:[]
};
}
render(){
return (
<Fragment>
<div>
<input type="text"
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)}
/>
<button onClick={this.handleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{
this.state.list.map((item,index) => {
return
<li
key={index}
onClick={this.handleItemDelete.bind(this,index)}
>
{item}
</li>
})
}
</ul>
</Fragment>
);
}
handleInputChange(e){
// console.log(e);//返回事件对象
// console.log(e.target);//返回input的dom节点
// console.log(e.target.value);//返回input的dom节点的value
// this.state.inputValue=e.target.value;
//1、会报错,因为this指向错误,给onChange事件修改this指向,使其指向当前组件TodoList
//2、报错Use setState(),不能直接赋值,需要使用setState方法
this.setState({
inputValue:e.target.value
});
}
handleBtnClick(){
this.setState({
list:[...this.state.list, this.state.inputValue],
inputValue:''
});
}
handleItemDelete(index){
//immutable:state不允许我们做任何改变
//不要直接state的内容,而要拷贝一个副本进行修改后,再用setSate进行修改
//若直接改state的内容,之后在做react性能优化的时候会出现问题
//this.state.list.splice(index,1); //直接修改state内容,不好的写法
const list=[...this.state.list];
list.splice(index,1); //删除数组下标为index,长度为1
this.setState({
list:list
});
}
}
export default TodoList;
称为组件,
等称为普通元素{}
表示js语法,具体写法如下,写法一,多行注释
{/*输入框*/}
写法二,单行注释,不要写在一行,右括号会被识别为注释内容
{
//点击提交后,输入的内容会显示到列表上
}
className
进行增加,不可采用class
,因为会被识别为class
类关键词。hello world
时,显示为hello world
,会被自动转义(用处:防止脚本攻击),如果不想被转义掉
标签,想设置hello world
内容字体大小为h1
大小。则给
增加一个dangerouslySetInnerHTML
属性,将值设为{{__html:item}}
,第一个花括号表示一个js表达式,第二个括号表示js表达式中的js对象,__html
属性设为item。删除{item}
。
有扩大点击范围的作用,for
会被误认为js中的for循环,则需要将for
改为htmlFor
。TodoList.js(修改js文件)
...
<Fragment>
{/*输入框*/}
{
//点击提交后,输入的内容会显示到列表上
}
<div>
<label htmlFor="insertArea">输入内容</label>
<input
id="insertArea"
className="input"
type="text"
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)}
/>
<button onClick={this.handleBtnClick.bind(this)}>提交</button>
</div>
{/*显示列表*/}
<ul>
{
this.state.list.map((item,index) => {
return (
<li
key={index}
onClick={this.handleItemDelete.bind(this,index)}
dangerouslySetInnerHTML={{__html:item}}
>
</li>
);
})
}
</ul>
</Fragment>
...
TodoList.css(新增css文件)
div{
margin-left: 10px;
}
.input{
border: 1px solid red;
}
组件拆分
label input button
划分为一个组件,将ul
划分为一个组件TodoItem
组件,然后export
这个组件,然后在TodoList
组件引入组件传值
state
的值来完成,因此需要子组件调用父组件的handleItemDelete()
方法并传递index
参数,进行删除item,那么采用的方法是,通过属性传值的方法向子组件传递父组件的handleItemDelete()
方法和index
参数,使子组件能调用父组件的方法。2)向子组件传递父组件的方法时,要注意修改方法的this指向,使其指向父组件。TodoList.js(修改js文件)
...
import TodoItem from './TodoItem';
...
<ul>
{
this.state.list.map((item,index) => {
return (
<Fragment>
<TodoItem
content={item}
index={index}
deleteItem={this.handleItemDelete.bind(this)}
/>
{/*
*/}
</Fragment>
);
})
}
</ul>
...
TodoItem.js(新建js文件)
import React,{Component} from 'react';
class TodoItem extends Component{
constructor(props) {
super(props);
this.handleClick=this.handleClick.bind(this); //在复杂组件开发之中,这样写法能节约性能
}
render(){
return (
<li onClick={this.handlerClick}>{this.props.content}</li>
);
}
handlerClick(){
//this.props.deleteItem()实际是this.handleItemDelete()
//实际上是父组件的方法,所以需要将handleItemDelete的this指向改为指向父组件,在父组件修改代码,增加bind(this)
//若不修改handleItemDelete()指向,则会报错this.props.deleteItem不是一个function
this.props.deleteItem(this.props.index);
}
}
export default TodoItem;
constructor
函数统一this指向,能提高性能;const {content} =this.props
;this.setState()
参数写成一个箭头函数,使用新语法。箭头函数允许传入一个参数prevState
,表示修改数据之前的数据状态,能避免不小心修改state
的数据状态;key
值,否则会报错。目前先用index
作为key
值是一种不靠谱的写法,具体之后虚拟dom部分会具体说明。注意,必须把key
值写在最外层上;TodoList.js(修改js文件)
import React, { Component, Fragment } from 'react';
import './css/TodoList.css';
import TodoItem from './TodoItem';
//render return 只能返回一个整体,不能返回多个同级节点
// 占位符,不显示
class TodoList extends Component{
//最优先执行函数
constructor(props) {
super(props);//调用父类的构造函数
//定义数据
//组件的初始化状态
this.state={
inputValue:'hello',
list:[]
};
//优化代码:this指向统一化,提高性能,只需要修改一次作用域即可
this.handleInputChange=this.handleInputChange.bind(this);
this.handleBtnClick=this.handleBtnClick.bind(this);
this.handleItemDelete=this.handleItemDelete.bind(this);
}
render(){
return (
<Fragment>
{/*输入框*/}
{
//点击提交后,输入的内容会显示到列表上
}
<div>
<label htmlFor="insertArea">输入内容</label>
<input
id="insertArea"
className="input"
type="text"
value={this.state.inputValue}
onChange={this.handleInputChange}
/>
<button onClick={this.handleBtnClick}>提交</button>
</div>
{/*显示列表*/}
<ul>
{this.getTodoItem()}
</ul>
</Fragment>
);
}
getTodoItem() {
return this.state.list.map((item,index) => {
return (
<Fragment key={index}>
<TodoItem
content={item}
index={index}
deleteItem={this.handleItemDelete}
/>
{/*
*/}
</Fragment>
);
});
}
handleInputChange(e){
// console.log(e);//返回事件对象
// console.log(e.target);//返回input的dom节点
// console.log(e.target.value);//返回input的dom节点的value
// this.state.inputValue=e.target.value;
//1、会报错,因为this指向错误,给onChange事件修改this指向,使其指向当前组件TodoList
//2、报错Use setState(),不能直接赋值,需要使用setState方法
// this.setState({
// inputValue:e.target.value
// });
//优化代码
//新语法支持传入一个函数,使用es6简写箭头函数
const value=e.target.value;
//写函数使setState变成异步,提升其性能,具体之后介绍虚拟dom会说明
this.setState(() => ({
inputValue:value
}));
}
handleBtnClick(){
// this.setState({
// list:[...this.state.list, this.state.inputValue],
// inputValue:''
// });
//prevState表示修改数据之前的数据状态,等价于当前的state
//此写法更靠谱,避免不小心修改state的数据状态
this.setState((prevState)=>({
list:[...prevState.list, prevState.inputValue],
inputValue:''
}));
}
handleItemDelete(index){
//immutable:state不允许我们做任何改变
//不要直接state的内容,而要拷贝一个副本进行修改后,再用setSate进行修改
//若直接改state的内容,之后在做react性能优化的时候会出现问题
//this.state.list.splice(index,1); //直接修改state内容,不好的写法
// const list=[...this.state.list];
// list.splice(index,1); //删除数组下标为index,长度为1
// this.setState({
// list:list
// });
this.setState((prevState)=>{
const list=[...prevState.list];
list.splice(index,1); //删除数组下标为index,长度为1
return {list};
});
}
}
export default TodoList;
TodoItem.js(修改js文件)
import React,{Component} from 'react';
class TodoItem extends Component{
constructor(props) {
super(props);
this.handleClick=this.handleClick.bind(this); //在复杂组件开发之中,这样写法能节约性能
}
render(){
const {content} =this.props; //优化代码 => content=this.props.content;
return (
<li onClick={this.handleClick}>{content}</li>
);
}
handleClick(){
//this.props.deleteItem()实际是this.handleItemDelete()
//实际上是父组件的方法,所以需要将handleItemDelete的this指向改为指向父组件,在父组件修改代码,增加bind(this)
//若不修改handleItemDelete()指向,则会报错this.props.deleteItem不是一个function
// this.props.deleteItem(this.props.index);
//优化代码
const {deleteItem, index}=this.props;
deleteItem(index);
}
}
export default TodoItem;