React.js基础精讲03

知识点
1、React编写TodoList功能
2、React响应式设计思想和事件绑定
3、实现TodoList新增删除功能
4、JSX语法补充
5、拆分组件与组件间传值
6、TodoList代码优化
7、react衍生思考


1、React编写TodoList功能

index.js(注释两行,增加两行)

...
// import App from './App';
import TodoList from './TodoList';
...
// ReactDOM.render(<App />, 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 只能返回一个整体,不能返回多个同级节点
// <Fragment> 占位符,不显示
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;

2、React响应式设计思想和事件绑定

  • state主要存储组件数据,改变页面内容只需要直接更改state数据即可;
  • JSX需要使用js表达式,需要使用 { } 进行包裹
  • 事件绑定时,需要使用bind(this) 进行作用域变更
  • 需要改变数据state时,需要使用setState()方法进行修改

TodoList.js(修改js文件)

import React, { Component, Fragment } from 'react';
import './css/TodoList.css';

//render return 只能返回一个整体,不能返回多个同级节点
// <Fragment> 占位符,不显示
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;

输入前
在这里插入图片描述
输入后
在这里插入图片描述

3、实现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>
    
  • 删除功能:给<li></li>绑定一个删除item的点击事件handleItemDelete,不要直接修改state的数据。

TodoList.js(修改js文件)

import React, { Component, Fragment } from 'react';
import './css/TodoList.css';

//render return 只能返回一个整体,不能返回多个同级节点
// <Fragment> 占位符,不显示
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;

4、JSX语法补充

  • <Fragment></Fragment>称为组件,<div></div>等称为普通元素
  • 写注释:JSX语法中的{}表示js语法,具体写法如下,
    写法一,多行注释
    {/*输入框*/}
    
    写法二,单行注释,不要写在一行,右括号会被识别为注释内容
    {
    	//点击提交后,输入的内容会显示到列表上
    }
    
  • class语法歧义:给节点添加class名,需要使用className进行增加,不可采用class,因为会被识别为class类关键词。
  • 标签被自动转义:输入内容为<h1>hello world</h1>时,显示为<h1>hello world</h1>,会被自动转义(用处:防止脚本攻击),如果不想被转义掉<h1></h1>标签,想设置hello world内容字体大小为h1大小。则给<li></li>增加一个dangerouslySetInnerHTML属性,将值设为{{__html:item}},第一个花括号表示一个js表达式,第二个括号表示js表达式中的js对象,__html属性设为item。删除{item}
  • for语法歧义:<label for="input_id"></label>有扩大点击范围的作用,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;
}

5、拆分组件与组件间传值

组件拆分

  • label input button划分为一个组件,将ul划分为一个组件
  • 新建一个TodoItem组件,然后export这个组件,然后在TodoList组件引入
  • 一个组件可能包含很多个组件,最终结构像树形结构一样
    在这里插入图片描述

组件传值

  • 父组件向子组件传值:父组件通过属性xxx进行传值,子组件通过{this.props.xxx}进行接收
  • 子组件向父组件传值:即调用父组件传递过来的方法。1)由于删除Item主要是修改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)}
					/>
					{/*<li 
						key={index} 
						onClick={this.handleItemDelete.bind(this,index)}
						dangerouslySetInnerHTML={{__html:item}}
					>
					</li>*/}
				</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;

6、TodoList代码优化

  • 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 只能返回一个整体,不能返回多个同级节点
// <Fragment> 占位符,不显示
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}
						/>
						{/*<li 
							key={index} 
							onClick={this.handleItemDelete.bind(this,index)}
							dangerouslySetInnerHTML={{__html:item}}
						>
						</li>*/}
					</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;

7、React衍生思考

  • js、jq都是命令式开发,react.js是声明式开发,面向数据编程,节约了大量dom操作代码。
  • react.js可以与其他框架(jquery、vue等框架)并存,不会影响其他框架代码。
  • react.js是组件化开发,可提高重用性。
  • react.js是单向数据流开发,父组件可以向子组件传值,但是子组件只能使用这个值,不能去改变这个值,一旦改变会报错。单向数据流的应使得代码的维护变得方便许多。
  • react.js是一个视图层的框架。在一些大型复杂的项目开发中,如果想要组件1和组件2进行通信,组件1需要调用上一层组件的方法进行数据传值,一直往上传递,直到根组件,然后再由根组件往右边一直向下进行属性传值,这个过程会产生很多不必要的代码。所以做大型项目时,不能只使用react.js框架,还需要配合一些数据层框架,解决组件之间的复杂传值问题。
    在这里插入图片描述
  • react.js采用函数式编程,便于维护代码,便于前端自动化测试。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值