React框架基础 - 2、简单学生管理系统案例

613 阅读8分钟

笔记来源:拉勾教育 - 大前端就业集训营

文章内容:学习过程中的笔记、感悟、和经验

管理系统

gtq8W6.md.png

创建项目

  • 创建项目,这里使用脚手架快速创建
  • 初始化项目(删除不需要文件、修改响应内容)

组件拆分

整个页面拆分为三个组件,分别是

  • 顶部标题部分
  • 左侧添加学生
  • 右侧学生列表
<!-- public/index.html 页面HTML文件 -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <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"
    />
    <!-- 引入两个CSS文件 -->
    <link rel="stylesheet" href="%PUBLIC_URL%/base.css">
    <link rel="stylesheet" href="%PUBLIC_URL%/bootstrap.min.css">
    <title>React App</title>
  </head>
  <body>
    <!-- React挂载点 -->
    <div id="root"></div>
  </body>
</html>
// src/App.js  App组件

// 引入React组件和三个页面子组件
import {Component} from 'react'
import Title from './Components/title'
import Left from './Components/left'
import Right from './Components/right'

// App
class App extends Component {
  render() {
    return (
      <div className="container">
        {/* 三个组件 */}
        <Title />
        <Left />
        <Right />
      </div>
    )
  }
}

// 导出
export default App
// src/Components/title.js  顶部标题

// 顶部标题,因为是固定内容,所以写成函数组件
function Title() {
  return (
    // 结构,使用老师提供的结构
    <div className="col-md-12">
      <h3 className="title">学生信息管理</h3>
    </div>
  )
}

// 导出
export default Title
// src/Components/left.js  左侧添加学生组件

// raact组件
import {Component} from 'react'

// 左侧添加学生组件
class Left extends Component {
  render() {
    return (
      // 结构,使用老师提供的结构
      <div className="col-md-5">
        <form>
          <div className="form-group">
            <label>学号</label>
            <input type="number" className="form-control" placeholder="请输入 学号" />
          </div>
          <div className="form-group">
            <label>姓名</label>
            <input type="text" className="form-control" placeholder="请输入姓 名" />
          </div>
          <div className="form-group">
            <label>性别&nbsp;&nbsp;</label>
            <label className="checkbox-inline">
              <input type="radio" value="男" /></label>
            <label className="checkbox-inline">
              <input type="radio" value="女" /></label>
          </div>
          <div className="form-group">
            <label>年龄</label>
            <input type="text" className="form-control" placeholder="请输入姓 名" />
          </div>
          <div className="form-group">
            <label>入学时间</label>
            <input className="form-control" type="date" />
          </div>
          <div className="form-group">
            <label>爱好</label>
            <div className="checkbox">
              <label>
                <input type="checkbox" value="足球" /> 足球
              </label>
            </div>
            <div className="checkbox">
              <label>
                <input type="checkbox" value="篮球" /> 篮球
              </label>
            </div>
            <div className="checkbox">
              <label>
                <input type="checkbox" value="橄榄球" /> 橄榄球
              </label>
            </div>
          </div>
          <div className="form-group">
            <label>所属学院</label>
            <select className="form-control">
              <option value="大前端">大前端</option>
              <option value="Java">Java</option>
              <option value="python">python</option>
            </select>
          </div>
          <button type="submit" className="btn btn-default">添加</button>
        </form>
    </div>
    )
  }
}

// 导出
export default Left
// src/Components/right.js  右侧学生列表

// react组件
import {Component} from 'react'

// 右侧学生列表
class Right  extends Component {
  render() {
    return (
      // 使用老师提供的结构
      <div className="col-md-6 col-md-offset-1">
        <table className="table table-striped table-hover">
          <thead>
            <tr>
              <th>学号</th>
              <th>姓名</th>
              <th>性别</th>
              <th>年龄</th>
              <th>入学时间</th>
              <th>爱好</th>
              <th>所属学院</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>01</td>
              <td>张三</td>
              <td></td>
              <td>20</td>
              <td>2020-08-02</td>
              <td>
                <span>足球</span>
              </td>
              <td>python</td>
              <td>
                <a href="www.baidu.com">删除</a>
                <a href="www.baidu.com">修改</a>
              </td>
            </tr>
          </tbody>
        </table>
        <p className="text-center">无学生信息</p>
        <p>总共有 50 个学生</p>
        <p>学生的平均年龄是 25</p>
      </div>
    )
  }
}

// 导出
export default Right

这里结构和css文件全部使用老师提供的文件,主要学习的是React处理数据

添加学生信息

除了复选框之外都可以放到state中进行管理

进行数据和结构的绑定

进行数据更新,修改数据调用方法,存到构造函数中,注意继承(super)

控件使用name作为键名

进行爱好复选框渲染,数据绑定和结构搭建

进行复选框的数据更新,不要直接修改,现将数据复制出来,整体进行替换

进行数据提交

提交的时候需要把表单中的信息整合在一起,这里需要筛选选中的爱好(只传递title)

将全部数据存储到App中

当点击提交后,将数据提交给App,利用单向数据流

提交后还原数据(需要注意单选框可能需要更改属性,复选框可能需要深拷贝才能正常使用)

注意setstate是异步的,使用回调的方式避免

// src/App.js  App组件添加学生状态数据,创建添加学生方法并把这个方法下放给left(添加学生组件)

// 引入React组件和三个页面子组件
import {Component} from 'react'
import Title from './Components/title'
import Left from './Components/left'
import Right from './Components/right'

// App
class App extends Component {
  state = {
    // 学生信息数组
    students: [
      // 书写两个静态学生数据
      {
        age: "10",
        college: "Java",
        hobbys: ["足球", "橄榄球"],
        name: "cwn",
        number: "001",
        sex: "男"
      },
      {
        age: "12",
        college: "大前端",
        hobbys: ["足球"],
        name: "stt",
        number: "002",
        sex: "女"
      }
    ]
  }
  // 添加学生方法
  // 参数1:提交上来的学生数据
  // 参数2:回调函数
  addStudent = (student, callback) => {
    // 深拷贝学生全部数据
    const students =JSON.parse(JSON.stringify(this.state.students))
    // 把新的学生信息添加进来
    students.push(student)
    // 用新的学生数据替换旧的数据,然后调用回调函数
    this.setState({students}, callback())
  }
  render() {
    return (
      <div className="container">
        {/* 三个组件 */}
        <Title />
        {/* 把添加学生方法下放给组件 */}
        <Left add={this.addStudent}/>
        <Right students={this.state.students} />
      </div>
    )
  }
}

// 导出
export default App
// src/Components/left.js  添加学生组件 - 创建学生状态数据、结构绑定数据、实现数据更改、实现数据提交

// raact组件
import {Component} from 'react'

// 左侧添加学生组件
class Left extends Component {
  // 学生信息(默认)
  state = {
    number: '',
    name: '',
    sex: '男',
    age: '',
    college: '大前端',
    hobbys: [
      {id: 0, title: '足球', isChecked: false},
      {id: 1, title: '篮球', isChecked: false},
      {id: 2, title: '橄榄球', isChecked: false},
    ]
  }
  // 保存默认状态数据
  rawDate = JSON.parse(JSON.stringify(this.state))
  // 除了爱好之外其他信息输入事件函数
  // 参数:事件对象
  chageState = (e) => {
    // 修改数据
    this.setState({
      [e.target.name]: e.target.value
    })
  }
  // 爱好修改事件
  // 参数:id - 修改的爱好对应id(这里id对应下标),e - 事件对象
  changeCollege = (id, e) => {
    // 深拷贝爱好数据
    const hobbys = JSON.parse(JSON.stringify(this.state.hobbys))
    // 修改相应数据
    hobbys[id].isChecked = e.target.checked
    // 修改状态数据
    this.setState({hobbys})
  }
  // 表单提交事件
  // ev - 事件对象
  submit = (ev) => {
    // 阻止默认行为避免跳转
    ev.preventDefault()
    // 深拷贝爱好数据
    let hobbys = JSON.parse(JSON.stringify(this.state.hobbys))
    // 把爱好数据选中的title单独组成数组
    hobbys = hobbys.filter(hobby => hobby.isChecked).map(hobby => hobby.title)
    // 调用App传递过来的方法添加学生,穿一个回调函数让添加完之后恢复默认数据
    this.props.add({...this.state,hobbys},() => this.setState(this.rawDate))
    
  }
  render() {
    return (
      // 结构,使用老师提供的结构
      <div className="col-md-5">
        <form onSubmit={this.submit}>
          <div className="form-group">
            <label>学号</label>
            {/* 使用name获取对应的数据名,绑定数据,添加onchange方法  下面同理 */}
            <input name='number' type="number" className="form-control" placeholder="请输入学号" value={this.state.number} onChange={this.chageState} />
          </div>
          <div className="form-group">
            <label>姓名</label>
            <input name='name' type="text" className="form-control" placeholder="请输入姓名" value={this.state.name} onChange={this.chageState} />
          </div>
          <div className="form-group">
            <label>性别&nbsp;&nbsp;</label>
            <label className="checkbox-inline">
              <input name='sex' type="radio" value="男" checked={this.state.sex === '男'} onChange={this.chageState}/></label>
            <label className="checkbox-inline">
              <input name='sex' type="radio" value="女" checked={this.state.sex === '女'} onChange={this.chageState}/></label>
          </div>
          <div className="form-group">
            <label>年龄</label>
            <input name='age' type="text" className="form-control" placeholder="请输入年龄" value={this.state.age} onChange={this.chageState}/>
          </div>
          {/* 爱好复选框和前面有区别,但基本逻辑相同 */}
          <div className="form-group">
            <label>爱好</label>
            {this.state.hobbys.map(hobby => {
              return (
                <div className="checkbox" key={hobby.id}>
                  <label>
                    <input type="checkbox" value={hobby.title} onChange={this.changeCollege.bind(this, hobby.id)} checked={hobby.isChecked}/> {hobby.title}
                  </label>
                </div>
              )
            })}
          </div>
          <div className="form-group">
            <label>所属学院</label>
            <select name='college' className="form-control" value={this.state.college} onChange={this.chageState}>
              <option value="大前端">大前端</option>
              <option value="Java">Java</option>
              <option value="python">python</option>
            </select>
          </div>
          <button type="submit" className="btn btn-default">添加</button>
        </form>
    </div>
    )
  }
}

// 导出
export default Left

学生信息展示

学生信息数据由App提供

list组件遍历全部学生数据,创建结构,绑定数据

爱好需要二次遍历

老师演示的没有入学日期 ,所以删除入学日期

学生信息删除

可以提前添加一些静态数据用作操作演示

在App组件中定义删除方法

在执行删除之前先询问 - window.confirm

list组件中触发方法,传递值

现将全部数据拿出,然后找到需要删除的数据,最后用这个数据替换掉原数据

使用findIndex找到要删除的位置

// src/App.js   创建删除学生方法,把原始学生数据和删除方法传递给组件

// 引入React组件和三个页面子组件
import {Component} from 'react'
import Title from './Components/title'
import Left from './Components/left'
import Right from './Components/right'

// App
class App extends Component {
  state = {
    // 学生信息数组
    students: [
      // 书写两个静态学生数据
      {
        age: "10",
        college: "Java",
        hobbys: ["足球", "橄榄球"],
        name: "cwn",
        number: "001",
        sex: "男"
      },
      {
        age: "12",
        college: "大前端",
        hobbys: ["足球"],
        name: "stt",
        number: "002",
        sex: "女"
      }
    ]
  }
  // 添加学生方法
  // 参数1:提交上来的学生数据
  // 参数2:回调函数
  addStudent = (student, callback) => {
    // 深拷贝学生全部数据
    const students =JSON.parse(JSON.stringify(this.state.students))
    // 把新的学生信息添加进来
    students.push(student)
    // 用新的学生数据替换旧的数据,然后调用回调函数
    this.setState({students}, callback())
  }
  // 删除学生方法
  // 参数为要删除学生的学号
  removeStudent = (number) => {
    // 深拷贝学生数组
    const students = JSON.parse(JSON.stringify(this.state.students))
    // 寻找到对应学好学生的索引值
    const index = students.findIndex(s => s.number === number)
    // 调用数组方法删除学生
    students.splice(index, 1)
    // 把更新后的数据替换进去
    this.setState({students})
  }
  render() {
    return (
      <div className="container">
        {/* 三个组件 */}
        <Title />
        {/* 把添加学生方法下放给组件 */}
        <Left add={this.addStudent}/>
        {/* 把学生数据和删除学生方法传递给学生列表 */}
        <Right students={this.state.students} remove={this.removeStudent} />
      </div>
    )
  }
}

// 导出
export default App
// src/Components/right.js  遍历全部学生数据,创建结构绑定数据,添加删除按钮点击事件执行app传递过来的删除学生方法// react组件import {Component} from 'react'// 右侧学生列表class Right  extends Component {  // 删除按钮点击事件  // 参数1:删除学生学号  removeClick = (number, e) => {    // 禁止默认行为    e.preventDefault()    // 删除之前要先确认一次    if (window.confirm('您确认要删除吗?')) {      // 确认删除调用删除学生方法      this.props.remove(number)    }  }  render() {    return (      // 使用老师提供的结构      <div className="col-md-6 col-md-offset-1">        <table className="table table-striped table-hover">          <thead>            <tr>              <th>学号</th>              <th>姓名</th>              <th>性别</th>              <th>年龄</th>              <th>入学时间</th>              <th>爱好</th>              <th>所属学院</th>              <th>操作</th>            </tr>          </thead>          <tbody>            {/* 遍历全部学生创建结构 */}            {this.props.students.map(student => {              return (                <tr key={student.number}>                  <td>{student.number}</td>                  <td>{student.name}</td>                  <td>{student.sex}</td>                  <td>{student.age}</td>                  <td>                    {student.hobbys.map((hobby, index) => {                      return (<span key={index}>{hobby}</span>)                    })}                  </td>                  <td>{student.college}</td>                  <td>                    {/* 删除按钮添加点击事件 */}                    <a href="www.lagou.com" onClick={this.removeClick.bind(this,student.number)}>删除</a>                    <a href="www.lagou.com">修改</a>                  </td>                </tr>              )            })}          </tbody>        </table>        <p className="text-center">无学生信息</p>        <p>总共有 50 个学生</p>        <p>学生的平均年龄是 25</p>      </div>    )  }}// 导出export default Right

计算平均值等

修改功能暂时不能实现,删除掉

无学生信息判断当前学生信息是否为空,有学生信息则不显示

学生个数直接获取即可

平均年龄需要计算全部学生的平均值,可能需要取整操作

// src/Components/right.js  完善其他功能:无学生信息动态现实和隐藏、总人数、平均年龄// react组件import {Component} from 'react'// 右侧学生列表class Right  extends Component {  // 删除按钮点击事件  // 参数1:删除学生学号  removeClick = (number, e) => {    // 禁止默认行为    e.preventDefault()    // 删除之前要先确认一次    if (window.confirm('您确认要删除吗?')) {      // 确认删除调用删除学生方法      this.props.remove(number)    }  }  // 计算平均值方法  averageAge = () => {    // 累加器    let sum = 0    // 遍历全部学生实现年龄累加    this.props.students.map(s => {      sum += Number(s.age)    })    // 返回平均值    return Math.floor(sum /this.props.students.length)  }  render() {    return (      // 使用老师提供的结构      <div className="col-md-6 col-md-offset-1">        <table className="table table-striped table-hover">          <thead>            <tr>              <th>学号</th>              <th>姓名</th>              <th>性别</th>              <th>年龄</th>              <th>入学时间</th>              <th>爱好</th>              <th>所属学院</th>              <th>操作</th>            </tr>          </thead>          <tbody>            {/* 遍历全部学生创建结构 */}            {this.props.students.map(student => {              return (                <tr key={student.number}>                  <td>{student.number}</td>                  <td>{student.name}</td>                  <td>{student.sex}</td>                  <td>{student.age}</td>                  <td>                    {student.hobbys.map((hobby, index) => {                      return (<span key={index}>{hobby}</span>)                    })}                  </td>                  <td>{student.college}</td>                  <td>                    {/* 删除按钮添加点击事件 */}                    <a href="www.lagou.com" onClick={this.removeClick.bind(this,student.number)}>删除</a>                    <a href="www.lagou.com">修改</a>                  </td>                </tr>              )            })}          </tbody>        </table>        {/* 使用三元表达式动态设置显示和隐藏 */}        {this.props.students.length > 0 ? null : <p className="text-center">无学生信息</p>}        {/* 直接使用长度即可 */}        <p>总共有 {this.props.students.length} 个学生</p>        {/* 调用方法获取平均值 */}        <p>学生的平均年龄是 {this.averageAge()}</p>      </div>    )  }}// 导出export default Right