1 使用Reat编写Todolist功能
TodoList.js
import React, { Component, Fragment } from 'react';
import './style.css';
class TodoList extends Component {
//ECM6 继承语法
//继承props方法
constructor(props) {
super (props);
this.state = {
inputValue: '',
list: []
}
}
//input 中的内容存储在 inputValue中
//JSX语法
render() {
return (
<Fragment>
<div>
{/* 这是一个注释 */}
<label htmlFor="insertArea">输入内容:</label>
<input
id="insertArea"
className='input' //使用class
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)} //在这里加this
/>
<button onClick={this.handleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return <li
key={ index }
onClick={this.hanleItemDelete.bind(this, index)}
//不转义显示
dangerouslySetInnerHTML = {{__html: item}}
>
{ item }
</li> // 用index做key值不好
})
}
</ul>
</Fragment>
)
}
handleInputChange(e) {
this.setState({
inputValue: e.target.value
})
//console.log(this)
//this.input.inputValue = e.target.value; //this指向不对哦
//console.log(e.target.value); //获得我们输入的内容
}
handleBtnClick() {
this.setState({
list: [...this.state.list, this.state.inputValue],
//把原来数组的内容展开,再增加新的值
inputValue: ''
})
}
hanleItemDelete(index) {
// immutable
// state 不允许我们做任何改变
//this.state.list.splice(index, 1); --> 不能这样写
const list = [...this.state.list]; //拷贝一份
list.splice(index, 1);
this.setState({
list: list
})
}
}
export default TodoList;
2 React中的响应式设计思想和事件绑定
React中:不关注DOM只关注数据即可
import React, { Component, Fragment } from 'react';
class TodoList extends Component {
//ECM6 继承语法
//继承props方法
constructor(props) {
super (props);
this.state = {
inputValue: '',
list: []
}
}
//input 中的内容存储在 inputValue中
//JSX语法
render() {
return (
<Fragment>
<div>
<input
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)} //在这里加this
/>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>learn English</li>
</ul>
</Fragment>
)
}
handleInputChange(e) {
this.setState({
inputValue: e.target.value
})
//console.log(this)
//this.input.inputValue = e.target.value; //this指向不对哦
//console.log(e.target.value); //获得我们输入的内容
}
}
export default TodoList;
如上是我们编写的Todolist 的一段代码
我们需要注意的有:
1.在React中,编写在元素间的变量要使用{}。(JSX语法)
2.各个元素的状态,我们可以通过继承父类的方法props,然后使用this.state获取元素中的内容。
3.在使用对应的事件,进行一个事件绑定在调用函数的时候,我们要注意的是this的指向,因此我们通过ECM6的bind方法,预先改变该处理函数的一个this指向。
如上述代码中的:handleInputChange(this),我们需要的是组件的this, 来获得其中的值,并修改
4.我们不能通过this.input.inputValue来直接更改值,在React中给我们提供了方法:setState
如下:
this.setState({
inputValue: e.target.value //inputValue中的值和e.target.value中的值相等
})
3 实现TodoList新增删除功能
以下是实现list一个增加和删除的代码
import React, { Component, Fragment } from 'react';
class TodoList extends Component {
//ECM6 继承语法
//继承props方法
constructor(props) {
super (props);
this.state = {
inputValue: '',
list: []
}
}
//input 中的内容存储在 inputValue中
//JSX语法
render() {
return (
<Fragment>
<div>
<input
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)} //在这里加this
/>
<button onClick={this.handleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return <li
key={ index }
onClick={this.hanleItemDelete.bind(this, index)}
>
{ item }
</li> // 用index做key值不好
})
}
</ul>
</Fragment>
)
}
handleInputChange(e) {
this.setState({
inputValue: e.target.value
})
//console.log(this)
//this.input.inputValue = e.target.value; //this指向不对哦
//console.log(e.target.value); //获得我们输入的内容
}
handleBtnClick() {
this.setState({
list: [...this.state.list, this.state.inputValue],
//把原来数组的内容展开,再增加新的值
inputValue: ''
})
}
hanleItemDelete(index) {
// immutable
// state 不允许我们做任何改变
//this.state.list.splice(index, 1); --> 不能这样写
const list = [...this.state.list]; //拷贝一份
list.splice(index, 1);
this.setState({
list: list
})
}
}
export default TodoList;
3.1 新增li标签
我们要获得List中的值,并且显示在网页上,我们使用数组的map方法进行一个映射
ul标签中
<ul>
{
this.state.list.map((item, index) => {
return <li
key={ index }
onClick={this.hanleItemDelete.bind(this, index)}
>
{ item }
</li> // 用index做key值不好
})
}
</ul>
新增的事件函数
handleBtnClick() {
this.setState({
list: [...this.state.list, this.state.inputValue],
//把原来数组的内容展开,再增加新的值
inputValue: ''
})
}
(1)新增li标签
如以上代码,我们使用数组的map方法来返回需要显示的内容。
需要注意的是,每个li必须含有一个关键字 key 并且必须是不同的,在这里我们使用了index,但是在React中,并不推荐使用Index,如果不使用的话,页面就会发出一个警告。
(2)添加按键事件方法
我们要改变的是List中的值,
我们同样使用了setState方法,
list: [...this.state.list, this.state.inputValue],
其中...this.state.list,
获取原来的值,this.state.inputValue
是获取输入框中的内容
之后,我们将输入框中的内容清除。
3.2 删除li标签
//ul标签中
<ul>
{
this.state.list.map((item, index) => {
return <li
key={ index }
onClick={this.hanleItemDelete.bind(this, index)}
>
{ item }
</li> // 用index做key值不好
})
}
</ul>
删除事件方法:
hanleItemDelete(index) {
// immutable
// state 不允许我们做任何改变
//this.state.list.splice(index, 1); --> 不能这样写
//拷贝一份
list.splice(index, 1);
this.setState({
list: list
})
}
我们需要做的是:单击相应的内容,相应的内容就会删除。
- 首先,我们获取对应的下标,通过bind方法来进行传参
- 然后,我们创建一个新的list 拷贝 const list = […this.state.list]; 拷贝一份
- 之后,通过数组的方法splice进行一个删除操作,
- 最后,我们使用setState的方法来进行一个重新的赋值
【注】再次强调:state 不允许我们做任何改变,每次更改,必须使用setState方法
因此我们不能这样写 this.state.list.splice(index, 1);
。这是错误的写法
在React中,我们将其称之为immutable规则。
4 拆分组件与组件之间传值
顶层组件:Todolist.js
其它都是子组件:TodoItem.js
我们将Todolist中组件进行一个分离,上面是按钮,下面是现实的li标签
当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。
Todolist.js
import React, { Component, Fragment } from 'react';
import './style.css';
import TodoItem from './TodoItem';
class TodoList extends Component {
//ECM6 继承语法
//继承props方法
constructor(props) {
super (props);
this.state = {
inputValue: '',
list: []
}
}
//input 中的内容存储在 inputValue中
//JSX语法
render() {
return (
<Fragment>
<div>
{/* 这是一个注释 */}
<label htmlFor="insertArea">输入内容:</label>
<input
id="insertArea"
className='input' //使用class
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)} //在这里加this
/>
<button onClick={this.handleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return (
<TodoItem
content={item}
index={index}
deleteItem = {this.hanleItemDelete.bind(this)} //传递方法
/>
)
})
}
</ul>
</Fragment>
)
}
handleInputChange(e) {
this.setState({
inputValue: e.target.value
})
//console.log(this)
//this.input.inputValue = e.target.value; //this指向不对哦
//console.log(e.target.value); //获得我们输入的内容
}
handleBtnClick() {
this.setState({
list: [...this.state.list, this.state.inputValue],
//把原来数组的内容展开,再增加新的值
inputValue: ''
})
}
hanleItemDelete(index) {
// immutable
// state 不允许我们做任何改变
//this.state.list.splice(index, 1); --> 不能这样写
const list = [...this.state.list]; //拷贝一份
list.splice(index, 1);
this.setState({
list: list
})
}
}
export default TodoList;
TodoItem.js
import React,{Component}from 'react';
class TodoItem extends Component {
constructor(props) {
super (props);
//使用这种写法,节约性能
this.handleClick = this.handleClick.bind(this);
}
render() {
return <li key={1} onClick={this.handleClick}>{this.props.content}</li>
}
//子组件调用父组件的方法来修改内容
handleClick(){
this.props.deleteItem(this.props.index);
}
}
export default TodoItem;
4.1 父组件传值给子组件并使用
(1)父组件传值给子组件
以上是所有的代码,主要发生改变的是如下代码:
<ul>
{
this.state.list.map((item, index) => {
return (
<TodoItem
content={item}
index={index}
deleteItem = {this.hanleItemDelete.bind(this)} //传递方法
/>
)
})
}
</ul>
在父节点中我们通过 <TodoItem />
来调用子组件
我们父节点传值,类似属性的方式 变量名 = {传值} 来给子组件传值,然后子组件就可以进行调用了。
(2)子组件使用父组件的值
TodoItem.js
import React,{Component}from 'react';
class TodoItem extends Component {
constructor(props) {
super (props);
//使用这种写法,节约性能
this.handleClick = this.handleClick.bind(this);
}
render() {
return <li key={1} onClick={this.handleClick}>{this.props.content}</li>
}
//子组件调用父组件的方法来修改内容
handleClick(){
this.props.deleteItem(this.props.index);
}
}
export default TodoItem;
我们使用父组件传值给我们,我们通过 this.props.变量名的方法来调用父组件的值。
4.2 子组件调用父组件的方法
(1)父组件传递方法给子组件
父组件传给子组件的方法类似于传值,但是在这里我们需要的是,这个方法的this必须指向的是父组件。因为父组件可以调用该方法,而子组件中没有该函数。
如果没有强制改变this的指向,那么在网页上报错,没有发现该函数,即无法调用。
deleteItem = {this.hanleItemDelete.bind(this)} //传递方法
(2)子组件调用父组件的方法
子组件在进行事件的时候,也要改变this的指向,我们通过如下的方法,比直接使用bind的方法更节约性能。
constructor(props) {
super (props);
//使用这种写法,节约性能
this.handleClick = this.handleClick.bind(this);
}
在调用父节点的方法的时候,我们同样通过,this.props
.方法进行调用
handleClick(){
this.props.deleteItem(this.props.index);
}
5 TodoList代码优化
优化主要使用了一些ECM6的语法:
1.大括号解构
const {content} = this.props;
2.箭头函数
3.将强制改变this指向的,写到最前面constructor中,提升效率,之后使用也方便
如: this.handleInputChange = this.handleInputChange.bind(this);
4.将setState中返回函数,而不是对象,异步操作,提升效率
5.使用prevState来表示之前的变量
6.如果在标签中,使用的代码过多,使用函数进行封装。
7.ul标签中的循环,要增加key值,要增加在最外层的元素上,不推荐使用index。
5.1 TodoItem.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;
//相当于 const content = this.props.conetent;
return (
<li onClick={this.handleClick}>
{content}
</li>
)
}
//子组件调用父组件的方法来修改内容
handleClick(){
const {deleteItem, index} = this.props;
deleteItem(index);
}
}
export default TodoItem;
5.2 TodoList.js
import React, { Component, Fragment } from 'react';
import TodoItem from './TodoItem';
import './style.css';
class TodoList extends Component {
//ECM6 继承语法
//继承props方法
constructor(props) {
super (props);
this.state = {
inputValue: '',
list: []
}
this.handleInputChange = this.handleInputChange.bind(this);
this.handleBtnClick = this.handleBtnClick.bind(this);
this.hanleItemDelete = this.hanleItemDelete.bind(this);
}
//input 中的内容存储在 inputValue中
//JSX语法
render() {
return (
<Fragment>
<div>
{/* 这是一个注释 */}
<label htmlFor="insertArea">输入内容:</label>
<input
id="insertArea"
className='input' //使用class
value={this.state.inputValue}
onChange={this.handleInputChange} //在这里加this
/>
<button onClick={this.handleBtnClick}>提交</button>
</div>
<ul>
{this.getTodoItem()} //调用函数方法来实现
</ul>
</Fragment>
)
}
getTodoItem() {
return this.state.list.map((item, index) => {
return (
<TodoItem
key={index}
content={item}
index={index}
deleteItem = {this.hanleItemDelete} //传递方法
/>
)
})
}
//返回对象 ES6 异步,性能提升 对象变成函数 注意target
handleInputChange(e) {
const {value} = e.target;
this.setState(() =>({
inputValue: value
}))
}
/*
handleInputChange(e) {
this.setState({
inputValue: e.target.value
})
}
*/
//prevState接受之前的参数
handleBtnClick() {
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputValue],
inputValue: ''
}))
}
/*
handleBtnClick() {
this.setState({
list: [...this.state.list, this.state.inputValue],
//把原来数组的内容展开,再增加新的值
inputValue: ''
})
} */
hanleItemDelete(index) {
this.setState((prevState) => {
const list = [...prevState.list];
list.splice(index, 1);
return {list /*等效于list:list*/}
})
}
/*
hanleItemDelete(index) {
const list = [...this.state.list];
list.splice(index, 1);
this.setState({
list: list
})
}
*/
}
export default TodoList;
运行结果:没有问题!!!!!!!!
6 React的概念及思考
6.1 开发方式
命令式编程(先做什么,再做什么) -> DOM操作(Jquery, 原生JS)
声明式开发(react自动根据数据构造页面DOM,这个数据可以理解为图纸。) -> React(节约DOM操作代码)
6.2 可以与其它框架并存(Jquery)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div><span>abc</span></div>
</body>
</html>
如在index.html中,React只对 <div id="root"></div>
生效(因为这里react是挂载在root上的。对于下面的
其它操作依然可以生效。
6.3 组件化开发
首字母大写是组件(首字母小写是标签元素。)
父组件 -> 传值给 子组件
子组件 -> 传值给 父组件 调用父组件的方法(子组件使用父组件传给的方法来修改父组件传的值(不能直接使用未传的方法来修改父组件传的值))
6.4 单向数据流
父组件可以向子组件传递内容,但是子组件只可以使用,但不可以改变(否则会报错,只读不可更改)
为了让开发方便,因为如果多个组件都可以对其进行修改,那么不知道是那个发生改变。
如果要改->通过子组件调用父组件的方法
6.5 视图层的框架
如果是紫色的传值,会很麻烦
因此在开发项目,不仅仅用到React,传值交给其它组件完成,React负责数据和页面渲染
因此,使用flask,Redux来辅助开发(数据层框架负责传值)
6.6 函数式编程
维护方便 (给需要测试函数一个参数,通过函数输出来判断函数是否运行正确。)
面向测试的开发流程->前端自动化测试->提高便捷性