React实例练习-响应式设计、数据绑定、列表渲染、删除单项

服务菜单

最好的学习就是在实战中成长,做一个《小姐姐服务菜单》应用,练习前面的知识和学习新知识

新建小姐姐组件

先在SRC的目录下面,新建一个文件Xiaojiejie.js

然后写一个基本的HTML结构

import React,{Component} from 'react';

class Xiaojiejie extends Component{
    render(){
        return (
            <div>
                <div><input /><button>增加服务</button></div>
                <ul>
                    <li>头部按摩</li>
                    <li>精油推背</li>
                </ul>
            </div>
            
        )
    }
}
export default Xiaojiejie

这个文件现在还没有功能,只是写完了一个小组件

我们把入口文件的<App/>组件换成Xiaojiejie组件

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App' 
import Xiaojiejie from './Xiaojiejie'

ReactDOM.render(<Xiaojiejie />,document.getElementById('root'))

组件外层包裹原则

上面的代码,如果去掉最外层的<div>,就会报错

Failed to compile
./src/Xiaojiejie.js
SyntaxError: D:\ReactDemo\demo01\src\Xiaojiejie.js: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>? (8:16)

React要求必须在一个组件的最外层进行包裹

Fragment标签

加上最外层的div,组件就完全正常

但有的时候,布局就偏不需要这个最外层的标签

  • 例如Flex布局时,外层不能有包裹元素

我们可以使用<Fragment>标签

import React,{Component,Fragment} from 'react';

class Xiaojiejie extends Component{
    render(){
        return (
            <Fragment>
                <div><input /><button>增加服务</button></div>
                <ul>
                    <li>头部按摩</li>
                    <li>精油推背</li>
                </ul>
            </Fragment>
            
        )
    }
}
export default Xiaojiejie

这时在浏览器的Elements中查看,发现已经没有外层的包裹了

响应式设计原理、数据的绑定方法

响应式设计和数据的绑定

React不建议直接操作DOM元素,而是通过数据进行驱动,改变界面中的效果

React会根据数据的变化,自动帮助你完成界面的改变

因此在写React代码时,你无需关注DOM相关的操作,只需关注数据的操作就可以了

  • 需求是增加小姐姐的服务项,就需要定义数据

  • 数据定义在Xiaojiejie组件中的constructor构造函数里

// js的构造函数,由其他任何函数执行
constructor(props) {
    super(props) // 调用父类的构造函数,固定写法
    this.state = {
        inputValue: '', // input中的值
        list: [] // 服务列表
    }
}

React中数据绑定和Vue差不多,采用字面量的形式(自己起的名字),使用{}来标注

inputvalue值绑定到input框中,说白了就是在JSX中使用js代码

<input value={this.state.inputValue}>

绑定事件

这时在界面的文本框中输入值,是没有任何变化的

这是因为我们强制绑定了inputValue的值

如果要想改变,需要绑定响应事件,改变inputValue的值

  • 绑定一个改变事件,这个事件执行inputChange()

<input value={this.state.inputValue} onChange={this.inputChange}>

根据函数执行顺序,在render()方法的下面建立inputChange()方法

inputChange(e){
  console.log(e);
}

inputChange(e){
  console.log(e.target.value);
}

这时候控制台打印输出值,看到获得了输入的值,想当然的认为直接改变inputValue的值就可以,但是会报错

inputChange(e){
  console.log(e.target.value);
  this.state.inputValue = e.target.value
}

TypeError: Cannot read property 'state' of undefined

这里犯了两个错误

  1. this指向不对,需要重新用bind设置一下指向(ES6语法)

<input value={this.state.inputValue} onChange={this.inputChange.bind(this)}>

  1. React中改变值需要用this.setState方法
inputChange(e){
  this.setState({
    inputValue:e.target.value
  })
}

整体Code

// Xiaojiejie.js
import React, { Component, Fragment } from 'react';

class Xiaojiejie extends Component {
    // js的构造函数,由其他任何函数执行
    constructor(props) {
        super(props) // 调用父类的构造函数,固定写法
        this.state = {
            inputValue: '', // input中的值
            list: [] // 服务列表
        }
    }
    render() {
        return (
            <Fragment>
                <div>
                    <input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
                    <button>增加服务</button>
                </div>
                <ul>
                    <li>头部按摩</li>
                    <li>精油推背</li>
                </ul>
            </Fragment>

        )
    }
    inputChange(e){
        // console.log(e.target.value);
        // this.state.inputValue = e.target.value
        this.setState({
            inputValue:e.target.value
          })
    }
}

export default Xiaojiejie

添加服务、列表渲染

让列表数据化

现在的列表是写死的两个<li>标签

要变成动态显示的,就需要把这个列表进行数据化,再用js代码,循环在页面上

先给list数组增加两个数组元素list:['基础按摩','精油推背']

有了数据,可以在JSX部分进行循环输出

render() {
  return (
    <Fragment>
      <div>
          <input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
          <button>增加服务</button>
      </div>
      <ul>
        {
          this.state.list.map((item,index)=>{
                return <li>{item}</li>
            })
        }
      </ul>
    </Fragment>
  )
}

这样数据就不再是固定的了,而是动态管理的

增加服务选项

要增加服务选项,需要在按钮上绑定一个方法this.addList(这个方法还没有,接下来建立)

<button onClick={this.addList.bind(this)}>增加服务</button>

// 增加服务的按钮响应方法
addList(){
    this.setState({
        list:[...this.state.list,this.state.inputValue]
    })
}

...是ES6的新语法,叫做扩展运算符

把list数组进行了分解,形成了新的数组,然后再进行组合,这种写法更简单和直观

list:[...this.state.list,this.state.inputValue]

等价于

list:['基础按摩','精油推背',this.state.inputValue]

解决Key值错误

Warning: Each child in a list should have a unique "key" prop.

在用map循环时,需要设置一个不同的值,这是React的要求

可以暂时用index+item的形式来实现

 <ul>
  {
      this.state.list.map((item,index)=>{
        // index 循环的索引,0,1,2,3,有几个元素它索引几遍
        // 但不能只把index作为key,因为项目中,你的索引可能有好多项,一个页面中有好多循环,这时用索引它就会重复,会报错
        // 索引+选项,重复的概率就会很小
          return <li key={index+item} >{item}</li>
      })
  }
</ul>

一般来说,input输入完后会清空

// 增加服务的按钮响应方法
addList(){
    this.setState({
        list:[...this.state.list,this.state.inputValue],
        inputValue:''
    })
}

删除服务选项

数组下标的传递

要删除一个东西,就要得到数组里的一个编号,也就是下标

传递下标要有事件产生,先来绑定一个双击事件

<ul>
    {
        this.state.list.map((item,index)=>{
            return (
                <li 
                    key={index+item}
                    onClick={this.deleteItem.bind(this,index)}
                >
                    {item}
                </li>
            )
        })
    }
</ul> 

为了看的更清晰,在return部分加了()

这样就可以换行编写JSX代码了,在onClick绑定了deleteItem方法

deleteItem

// 删除单项服务
deleteItem(index){
    console.log(index)
    let list = this.state.list
    // 从索引项开始删除,删除一位
    list.splice(index,1)
    this.setState({
        list:list
    })
}

获得了数据下标后,删除数据就变得容易起来

先声明一个局部变量,然后利用传递过来的下标删除数组中的值,最后用setState更新数据就行

这里有个坑需要踩,有的伙伴认为这样写也是正确的

deleteItem(index){
  this.state.list.splice(index,1)
  this.setState({
    list:this.state.list
  })
}

请记住React是禁止直接操作state的,虽然这样也管用,但是在后期的性能优化上会有很多麻烦,一定不要这样操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值