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(, 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;

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

输入前
React.js基础精讲03_第1张图片
输入后
在这里插入图片描述

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>
    
  • 删除功能:给
  • 绑定一个删除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;

4、JSX语法补充

  • 称为组件,
    等称为普通元素
  • 写注释:JSX语法中的{}表示js语法,具体写法如下,
    写法一,多行注释
    {/*输入框*/}
    
    写法二,单行注释,不要写在一行,右括号会被识别为注释内容
    {
    	//点击提交后,输入的内容会显示到列表上
    }
    
  • class语法歧义:给节点添加class名,需要使用className进行增加,不可采用class,因为会被识别为class类关键词。
  • 标签被自动转义:输入内容为

    hello world

    时,显示为

    hello world

    ,会被自动转义(用处:防止脚本攻击),如果不想被转义掉

    标签,想设置hello world内容字体大小为h1大小。则给
  • 增加一个dangerouslySetInnerHTML属性,将值设为{{__html:item}},第一个花括号表示一个js表达式,第二个括号表示js表达式中的js对象,__html属性设为item。删除{item}
  • for语法歧义:有扩大点击范围的作用,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组件引入
  • 一个组件可能包含很多个组件,最终结构像树形结构一样
    React.js基础精讲03_第2张图片

组件传值

  • 父组件向子组件传值:父组件通过属性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)}
					/>
					{/*
  • */
    } </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 只能返回一个整体,不能返回多个同级节点
    //  占位符,不显示
    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;
    

    7、React衍生思考

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

    你可能感兴趣的:(前端)