1801A React

一、初识React

课程目标

  1. React简介
  2. 脚手架
  3. hello world
  4. JSX语法

知识点

1.React简介

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站(https://www.instagram.com/)。做出来以后,发现这套东西很好用,就在2013年5月开源了(https://github.com/facebook/react)。由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。

官方网站:https://react.docschina.org/

英文官方网站:https://reactjs.org/

目前的版本: 16.13.1

github star数:149k (vue是164k)

npm下载量对比:https://npmcharts.com/compare/react,vue

2.脚手架

create-react-app官网:https://github.com/facebook/create-react-app

npm 从5.2版开始,增加了 npx 命令。

npx 还能避免全局安装的模块。

npx 将create-react-app下载到一个临时目录,使用以后再删除。所以,以后再次执行上面的命令,会重新下载create-react-app。

每次使用的都是最新版本,脚手架使用的频率并不高,一个大项目使用一次就够了。

npx create-react-app my-app
cd my-app
npm start

3.hello world

类组件:

import React, { Component } from 'react'

export default class Home extends Component {
  render() {
    return (
      <div>
        hello world!
      </div>
    )
  }
}

函数组件:

import React from 'react'

const Home = () => {
  return (
    <div>
      hello world!
    </div>
  )
}

export default Home

4.JSX语法

React发明了JSX,利用HTML语法来创建虚拟DOM。

React的核心机制之一就是可以在内存中创建虚拟的DOM元素。以此来减少对实际DOM的操作从而提升性能。

JSX 即Javascript XML,它是对JavaScript 语法扩展。

我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。

JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

我们可以在 JSX 中使用 JavaScript 表达式。表达式写在花括号 {} 中。

在 JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算) 表达式来替代。

HTML 语言直接写在 JavaScript 语言之中,这就是 JSX(JavaScript and XML) 的语法。JSX,是一种 JavaScript 的语法扩展,它允许 HTML 与 JavaScript 的混写。JSX是facebook为React框架开发的一套语法糖,语法糖又叫做糖衣语法,是指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用,它主要的目的是增加程序的可读性,从而减少程序代码错处的机会。JSX就是JS的一种语法糖,类似的还有CoffeeScript、TypeScript,最终它们都会被解析成JS才能被浏览器理解和执行,如果不解析浏览器是没有办法识别它们的,这也是所有语法糖略有不足的地方。

const element = <h1>Hello, world!</h1>;

上面这种看起来可能有些奇怪的标签语法既不是字符串也不是HTML,被称为 JSX,JSX带来的一大便利就是我们可以直接在JS里面写类DOM的结构,比我们用原生的JS去拼接字符串,然后再用正则替换等方式来渲染模板方便和简单太多了。推荐在 React 中使用 JSX 来描述用户界面。JSX 用来声明 React 当中的元素, 乍看起来可能比较像是模版语言,但事实上它完全是在 JavaScript 内部实现的。

你可以任意地在 JSX 当中使用 JavaScript 表达式,在 JSX 当中的表达式要包含在大括号里。例子如下:

const names = ['Jack', 'Tom', 'Alice'];
const element = (
  <div>
     { names.map(function (name) { return <div>Hello, {name}!</div>}) }
  </div>
);

在书写 JSX 的时候一般都会带上换行和缩进,这样可以增强代码的可读性。与此同时,推荐在 JSX 代码的外面扩上一个小括号,这样可以防止分号自动插入的bug。

上面我们声明了一个names数组,然后遍历names数组在前面加上Hello,生成了element数组。JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员。JSX 本身其实也是一种表达式,在编译之后,JSX 其实会被转化为普通的 JavaScript 对象。

授课思路

在这里插入图片描述

案例作业

1.阅读官网
2.使用脚手架创建项目
3.编写hello world页面
4.预习加加减减

二、加加减减

课程目标

  1. VSCode插件
  2. 添加样式
  3. 加加减减
  4. JSX语法

知识点

1.VSCode插件

ES7 React/Redux/GraphQL/React-Native snippets

快捷方式:
创建类组件: rcc
创建函数数据:raface

2.添加样式

使用className,而不是class

import React from 'react'

const Home = () => {
  return (
    <div className="m-title">
      hello world!
      <span className="m-name"></span>
    </div>
  )
}

export default Home

想使用sass同学可以安装node-sass

yarn add node-sass

把样式文件改成scss就行了

.m-title{
  color: red;
  .m-name{
    color: lightblue;
  }
}

3.加加减减

constructor构造函数在组件创建时执行,super代表调用父类的构造函数,Component就是父类。

state是状态的意思,类似于vue的里的data。

修改state使用setState方法。

onClick是React合成事件。

import React, { Component } from 'react'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }

  handleSub() {
    let { count } = this.state
    count--
    this.setState({
      count
    })
  }

  handleAdd() {
    let { count } = this.state
    count++
    this.setState({
      count
    })
  }

  render() {
    let { count } = this.state
    return (
      <div>
        <div>{count}</div>
        <button onClick={() => this.handleSub()}></button>
        <button onClick={() => this.handleAdd()}></button>
      </div>
    )
  }
}

在这里插入图片描述

4.事件系统

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

不会存在IE浏览器兼容性的问题。

在React底层,主要对合成事件做了两件事情:事件委派和自动绑定。

  1. 事件委派

React中并不是把事件处理函数绑定到当前DOM上,而是把所有的事件绑定到结构的最外层,使用统一的事件监听器。
这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。
组件挂载和卸载时,只是在统一事件监听器上插入删除一些对象。

  1. 自动绑定

在React组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前的组件。而且React会对这种引用缓存,以达到CPU和内存的最大优化。

vue里是使用@click绑定点击事件。

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.制作加加减减
3.添加样式
4.预习列表渲染

三、列表渲染

课程目标

  1. map方法
  2. 列表渲染
  3. key
  4. 学习资料

知识点

1.map方法

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

map() 方法按照原始数组元素顺序依次处理元素。

注意: map() 不会对空数组进行检测。

注意: map() 不会改变原始数组。

    let arr = [1, 2, 3]
    let arr2 = arr.map(item => item * 2)
    console.log(arr2)

2.列表渲染

在state里定义一个数组,包含id
把数组里的字段渲染到页面上
类似于vue里的v-for

import React, { Component } from 'react'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [
        {
          id: 0,
          title: '武侠'
        },
        {
          id: 1,
          title: '都市'
        },
        {
          id: 2,
          title: '科幻'
        }
      ]
    }
  }
  render() {
    let { list } = this.state

    let listDom = list.map(item => (
      <div key={item.id}>{item.title}</div>
    ))

    return (
      <div>
        {listDom}
      </div>
    )
  }
}

在这里插入图片描述

3.key

key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key。

这个 key 必须是每个元素唯一的标识。key可以在DOM 中的某些元素被增加或删除的时候帮助 React 识别哪些元素发生了变化,提高渲染性能。所以需要给数组每一个元素增加一个唯一的标识。
一般可以将后台返回的ID作为key值,因为后台返回的ID都是唯一的。

4.学习资料

官网列表渲染:
https://react.docschina.org/docs/lists-and-keys.html

菜鸟教程列表渲染:
https://www.runoob.com/react/react-lists-and-keys.html

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.列表渲染
3.预习条件渲染

四、条件渲染

课程目标

  1. 条件渲染dom
  2. 条件渲染样式
  3. key
  4. 学习资料

知识点

1.条件渲染dom

dom的显示和隐藏:hello world!的显示与隐藏。
文案的切换:按钮文案的变化。
属性的切换:控制密码的显示隐藏。
在vue里使用v-if v-show。

import React, { Component } from 'react'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      visible: false
    }
  }

  handleVisible() {
    let { visible } = this.state
    this.setState({
      visible: !visible
    })
  }

  render() {
    let { visible } = this.state
    return (
      <div>
        <input type={ visible ? "text" : "password" } ></input>
        <button onClick={() => this.handleVisible()}>{ visible ? '隐藏' : '显示' }</button>
        {
          visible ? <div>hello world!</div> : null
        }
      </div>
    )
  }
}

在这里插入图片描述

2.条件渲染样式

根据状态控制样式:tab切换的高亮显示。

import React, { Component } from 'react'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [
        {
          id: 0,
          title: '武侠'
        },
        {
          id: 1,
          title: '都市'
        },
        {
          id: 2,
          title: '科幻'
        }
      ],
      currentId: 0
    }
  }

  handleNav(currentId) {
    this.setState({
      currentId
    })
  }

  render() {
    let { list, currentId } = this.state

    let listDom = list.map(item => (
      <div key={item.id} className={ `m-nav-item ${currentId === item.id ? 'active' : ''}` } onClick={() => this.handleNav(item.id)}>{item.title}</div>
    ))

    return (
      <div>
        {listDom}
      </div>
    )
  }
}
.m-nav-item{height: 40px;line-height: 40px;border-bottom: 1px solid #ddd;text-align: center;}
.m-nav-item.active{background: lightblue;}

在这里插入图片描述

3.对话框

控制dom的显示和隐藏:dialog对话框。

div水平垂直居中。

import React, { Component } from 'react'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      visible: false
    }
  }

  handleVisible() {
    let { visible } = this.state
    this.setState({
      visible: !visible
    })
  }

  handleClose() {
    this.setState({
      visible: false
    })
  }

  render() {
    let { visible } = this.state
    return (
      <div>
        <button onClick={() => this.handleVisible()}>{ visible ? '隐藏' : '显示' }</button>
        {
          visible ? 
          <div className="m-dialog-wrap">
            <div className="m-dialog">
              <div className="m-dialog-header">
                <div className="m-dialog-title">添加</div>
                <div className="m-dialog-close" onClick={() => this.handleClose()}>X</div>
              </div>
            </div>
          </div> : null
        }
      </div>
    )
  }
}

.m-dialog-wrap{display: flex; position: fixed;top: 0;left: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 0.5);}
.m-dialog{margin: auto;min-width: 300px;min-height: 240px;background: #fff;border-radius: 10px;}
.m-dialog-header{display: flex;height: 40px;line-height: 40px;border-bottom: 1px solid #ddd;}
.m-dialog-title{flex: 1;padding: 0 0 0 10px;}
.m-dialog-close{width: 40px;text-align: center;cursor: pointer;}

在这里插入图片描述

4.学习资料

官网条件渲染:
https://react.docschina.org/docs/conditional-rendering.html

菜鸟教程条件渲染:
https://www.runoob.com/react/react-conditional-rendering.html

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.制作对话框
3.预习受控组件

五、受控组件

课程目标

  1. 什么是受控组件
  2. 文本框受控组件
  3. 如何获取受控组件的值
  4. 学习资料

知识点

1.什么是受控组件

在 HTML 中,表单元素(如<input>、 <textarea> 和 <select>)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。

我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

假设我们现在有一个表单,表单中有一个input标签,input的value值必须是我们设置在constructor构造函数的state中的值,然后,通过onChange触发事件来改变state中保存的value值,这样形成一个循环的回路影响。也可以说是React负责渲染表单的组件仍然控制用户后续输入时所发生的变化。

就像上面这样,input中的value值通过state值获取,onChange事件改变state中的value值,input中的value值又从state中获取。。。

类似于vue中的v-model

2.文本框受控组件

input框的值来至state,onChange事件修改state。

import React, { Component } from 'react'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      username: ''
    }
  }

  handleUsername(e) {
    this.setState({
      username: e.target.value
    })
  }

  render() {
    let { username } = this.state
    return (
      <div>
        <div>
          <input value={username} onChange={(e) => this.handleUsername(e)} placeholder="请输入用户名"></input>
        </div>
      </div>
    )
  }
}

在这里插入图片描述

3.如何获取受控组件的值

从state里解构。

import React, { Component } from 'react'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      username: '',
      password: ''
    }
  }

  handleUsername(e) {
    this.setState({
      username: e.target.value
    })
  }

  handlePasswrod(e) {
    this.setState({
      password: e.target.value
    })
  }

  handleLogin() {
    let { username, password } = this.state

    console.log(username, password)
  }

  render() {
    let { username, password } = this.state
    return (
      <div>
        <div>
          <input value={username} onChange={(e) => this.handleUsername(e)} placeholder="请输入用户名"></input>
        </div>
        <div>
          <input value={password} onChange={(e) => this.handlePasswrod(e)} placeholder="请输入密码" type="password"></input>
        </div>
        <button onClick={() => this.handleLogin()}>登录</button>
      </div>
    )
  }
}


在这里插入图片描述

4.学习资料

官网受控组件:
https://zh-hans.reactjs.org/docs/forms.html#controlled-components

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.制作对话框
3.预习受控组件

六、组件

课程目标

  1. 组件化思想
  2. 编写第一个组件
  3. 组件间的通讯
  4. iconfont

知识点

1.组件化思想

组件化就是把传统的一张大网页拆分成很多小组件,有些组件可以反复使用,有些组件也许只使用一次,拆分成很多组件的目的就是使页面解构更清晰更好维护,也方便多人协作开发一个大项目。

对比vue的单文件组件。

组件作为React的核心内容,是View的重要组成部分,每一个View页面都由一个或多个组件构成,可以说组件是React应用程序的基石。在React的组件构成中,按照状态来分可以分为有状态组件和无状态组件。
所谓无状态组件,就是没有状态控制的组件,只做纯静态展示的作用,无状态组件是最基本的组件形式,它由属性props和渲染函数render构成。由于不涉及到状态的更新,所以这种组件的复用性也最强。
有状态组件是在无状态组件的基础上增加了组件内部状态管理,有状态组件通常会带有生命周期lifecycle,用以在不同的时刻触发状态的更新,有状态组件被大量用在业务逻辑开发中。

在这里插入图片描述

2.编写第一个组件

创建components文件夹。
创建Icon.js文件。自定义组件的首字母大写,组件文件首字母也大写。

components/Icon.js:

import React, { Component } from 'react'

export default class Icon extends Component {
  render() {
    return (
      <span>子组件</span>
    )
  }
}

父组件引入子组件,并在JSX里使用子组件。

import React, { Component } from 'react'
import Icon from './components/Icon'

export default class Home extends Component {
  render() {
    return (
      <div>
        <Icon></Icon>
      </div>
    )
  }
}

在这里插入图片描述

3.组件间的通讯

父组件向子组件传递属性和方法。
子组件使用父组件传递过来的属性。
子组件调用父组件传递过来的方法。

父组件:

import React, { Component } from 'react'
import Icon from './components/Icon'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      visible: true
    }
  }

  handleVisible() {
    const { visible } = this.state
    this.setState({
      visible: !visible
    })
  }

  render() {
    const { visible } = this.state
    return (
      <div>
        <Icon name={ visible ? 'show' : 'hide' } className="m-eye-icon" onClick={ () => this.handleVisible() }></Icon>
        <Icon></Icon>
        <Icon name="home" className="m-home-icon"></Icon>
      </div>
    )
  }
}

子组件:

import React, { Component } from 'react'

export default class Icon extends Component {
  render() {
    let { name = 'zhanwei', className = '', onClick = () => {} } = this.props
    return (
      <span className={`icon iconfont icon-${name} ${className}`} onClick={onClick}></span>
    )
  }
}

在这里插入图片描述

4.iconfont

阿里巴巴矢量图标库:
https://www.iconfont.cn/

使用iconfont图标代替图片图标,能减少文件体积、降低服务器请求数,节约宽带资源。

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.编写控制密码显示隐藏的Icon组件
3.预习props类型检查

七、prop类型检查

课程目标

  1. 类型检查的意义
  2. Icon组件类型检查
  3. 常用的类型检查
  4. 学习资料

知识点

1.类型检查的意义

随着你的应用程序不断增长,你可以通过类型检查捕获大量错误。
React 内置了一些类型检查的功能。要在组件的 props 上进行类型检查,你只需配置特定的 propTypes 属性。
PropTypes 提供一系列验证器,可用于确保组件接收到的数据类型是有效的。
传入的 prop 值类型不正确时,JavaScript 控制台将会显示警告。
出于性能方面的考虑,propTypes 仅在开发模式下进行检查。

2.Icon组件类型检查

对prop做必要的类型检查。

components/Icon.js:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class Icon extends Component {
  render() {
    let { name = 'zhanwei', className = '', onClick = () => {} } = this.props
    return (
      <span className={`icon iconfont icon-${name} ${className}`} onClick={onClick}></span>
    )
  }
}

Icon.propTypes = {
  name: PropTypes.string.isRequired,  //name必须传string类型,且必须传
  className: PropTypes.string,   //className必须是string类型,可以不传
  onClick: PropTypes.func,       //onClick必须是函数类型
}

父组件:

import React, { Component } from 'react'
import Icon from './components/Icon'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      visible: true
    }
  }

  handleVisible() {
    const { visible } = this.state
    this.setState({
      visible: !visible
    })
  }

  render() {
    const { visible } = this.state
    return (
      <div>
        <Icon name={ visible ? 'show' : 'hide' } className="m-eye-icon" onClick={ () => this.handleVisible() }></Icon>
        <Icon name={1}></Icon>
        <Icon></Icon>
        <Icon name="home" onClick={1}></Icon>
      </div>
    )
  }
}

在这里插入图片描述

3.常用的类型检查

  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  children: PropTypes.element,

4.学习资料

使用 PropTypes 进行类型检查:
https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html#gatsby-focus-wrapper

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.给Icon组件添加类型检查
3.预习生命周期

八、生命周期

课程目标

  1. 什么是生命周期
  2. 挂载相关的生命周期
  3. 更新相关的生命周期
  4. shouldComponentUpdate

知识点

1.什么是生命周期

生命周期函数(钩子函数)通俗的说就是在某一时刻会被自动调用执行的函数。

生命周期(life cycle)的概念广泛运用于各行各业。从广义上来说,生命周期泛指自然界和
人类社会中各种客观事物的阶段性变化及其规律。自然界的生命周期,可分为出生、成长、成熟、
衰退直到死亡。

React 组件的生命周期根据广义定义描述,可以分为挂载、渲染和卸载这几个阶段。当渲染
后的组件需要更新时,我们会重新去渲染组件,直至卸载。
因此,我们可以把 React 生命周期分成两类:
1)当组件在挂载或卸载时;
2)当组件接收新的数据时,即组件更新时。

2.挂载相关的生命周期

componentWillMount,componentDidMount, render,componentWillUnmount

components/Icon.js:

import React, { Component } from 'react'

export default class Icon extends Component {
  componentWillUnmount() {
    console.log('卸载前')
  }
  render() {
    return (
      <div>
        子组件
      </div>
    )
  }
}

父组件:

import React, { Component } from 'react'
import Icon from './components/Icon'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      visible: true
    }
  }

  handleVisible() {
    let { visible } = this.state
    this.setState({
      visible: !visible
    })
  }

  componentWillMount() {
    console.log('挂载前(已经废弃)')
  }

  componentDidMount() {
    console.log('挂载后')
  }

  render() {
    console.log('渲染')
    let { visible } = this.state
    return (
      <div>
        <div>
          <button onClick={ () => this.handleVisible() }>按钮</button>
        </div>
        { visible ? <Icon></Icon> : null }
      </div>
    )
  }
}

在这里插入图片描述

3.更新相关的生命周期

componentWillUpdate、componentDidUpdate

父组件:

import React, { Component } from 'react'
import Icon from './components/Icon'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }

  handleAdd() {
    let { count } = this.state
    this.setState({
      count: count + 1
    })
  }

  componentWillUpdate() {
    console.log('父组件更新前(已废弃)')
  }

  componentDidUpdate() {
    console.log('父组件更新后')
  }

  render() {
    console.log('渲染父组件')
    let { count } = this.state
    return (
      <div>
        <div>
          {count}
        </div>
        <div>
          <button onClick={ () => this.handleAdd() }></button>
        </div>
        <Icon></Icon>
      </div>
    )
  }
}

子组件:

import React, { Component } from 'react'

export default class Icon extends Component {
  componentWillUpdate() {
    console.log('子组件更新前(已废弃)')
  }

  componentDidUpdate() {
    console.log('子组件更新后')
  }
  render() {
    console.log('渲染子组件')
    return (
      <div>
        子组件
      </div>
    )
  }
}

在这里插入图片描述

4.shouldComponentUpdate

用于性能优化,shouldComponentUpdate返回true则更新,返回false则不更新!
人为决定是否更新,避免了不必要的更新,从而优化了性能!

父组件:

import React, { Component } from 'react'
import Icon from './components/Icon'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0,
      visible: true
    }
  }

  handleAdd() {
    let { count } = this.state
    this.setState({
      count: count + 1
    })
  }

  handleVisible() {
    this.setState({
      visible: !this.state.visible
    })
  }

  render() {
    console.log('渲染父组件')
    let { count, visible } = this.state
    return (
      <div>
        <div>
          {count}
        </div>
        <div>
          <button onClick={ () => this.handleAdd() }></button>
          <button onClick={ () => this.handleVisible() }>{ visible ? '隐藏' : '显示'}</button>
        </div>
        <Icon visible={visible}></Icon>
      </div>
    )
  }
}

子组件:

import React, { Component } from 'react'

export default class Icon extends Component {

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.visible !== nextProps.visible) {
      return true
    } else {
      return false
    }
  }

  render() {
    let { visible } = this.props
    console.log('渲染子组件')
    return (
      <div>
        子组件:{visible ? '显示' : '隐藏' }
      </div>
    )
  }
}

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.练习挂载相关的生命周期
3.预习tab切换

九、tab切换

课程目标

  1. 展开项目,写接口
  2. 在挂载完生命周期里请求数据
  3. 开发子组件
  4. 开发父组件

知识点

1.展开项目,写接口

运行下面的命令可以展开项目,如何展开后报错,可以把node_modules删了重新装包

yarn eject

devServer里写接口:

//列表接口
app.get('/api/list', (req, res) => {
  res.send({
    code: 200,
    data: bookMallData,
    message: '列表'
  })
})

2.在挂载完生命周期里请求数据

axios语法。
生命周期。

import React, { Component } from 'react'
import axios from 'axios'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [],
      currentId: 0,
      currentList: []
    }
  }

  componentDidMount() {
    axios({
      url: '/api/list'
    }).then(res => {
      if (res.data.code === 200) {
        this.setState({
          list: res.data.data,
          currentList: res.data.data[0].list
        })
      }
    })
  }

  render() {
    return (
      <div className="m-wrap">
      </div>
    )
  }
}

3.开发子组件

Sidebar.js:

import React, { Component } from 'react'

export default class Sidebar extends Component {
  render() {
    let { list, currentId } = this.props
    let listDom = list.map(item => (
      <div key={item.id} 
        className={"m-sidebar-item " + (currentId === item.id ? 'active' : '')}
        onClick={() => this.props.onClick(item.id)}>{item.title}</div>
    ))
    return (
      <div className="m-sidebar">
        {listDom}
      </div>
    )
  }
}

List.js:

import React, { Component } from 'react'

export default class List extends Component {
  render() {
    let { currentList } = this.props

    let currentListDom = currentList.map(item => (
      <div key={item.id} className="m-list-item">
        <img src={item.avatar} className="m-img"></img>
        <div className="m-info">{item.title}</div>
      </div>
    ))

    return (
      <div className="m-list">
        {currentListDom}
      </div>
    )
  }
}

4.开发父组件

父组件:

import React, { Component } from 'react'
import axios from 'axios'
import Sidebar from './components/Sidebar'
import List from './components/List'

export default class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [],
      currentId: 0,
      currentList: []
    }
  }

  handleNav(currentId) {
    let { list } = this.state
    let currentList = list.find(item => item.id === currentId).list
    this.setState({
      currentId,
      currentList
    })
  }

  componentDidMount() {
    axios({
      url: '/api/list'
    }).then(res => {
      if (res.data.code === 200) {
        this.setState({
          list: res.data.data,
          currentList: res.data.data[0].list
        })
      }
    })
  }

  render() {
    let { list, currentId, currentList } = this.state
    return (
      <div className="m-wrap">
        <Sidebar list={list} currentId={currentId} onClick={(id) => this.handleNav(id)}></Sidebar>
        <List currentList={currentList}></List>
      </div>
    )
  }
}

在这里插入图片描述

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.练习tab切换
3.预习todolist

十、todolist

课程目标

  1. todolist是什么
  2. 在挂载完生命周期里请求数据
  3. 开发子组件
  4. 开发父组件

知识点

1.todolist是什么

管理用户要做的事件,可以添加事件,把事件标记为正在进行或已经完成,也可以删除事件。

http://www.todolist.cn/

2.父组件

管理子组件,核心代码都在父组件。

import React, { Component } from 'react'
import Header from './components/Header'
import List from './components/List'

export default class App extends Component {
  state = {
    list: []
  }

  //向list数组里添加数据
  handleAdd(title) {
    //解构
    let { list } = this.state

    list.push({
      id: Date.now(),  //取当前时间的时间戳
      title: title,
      checked: false,  //不打勾
    })

    //更新list数组
    this.setState({
      list
    })
  }

  //点击复选框事件
  handleCheck(e, id) {
    let list = this.state.list
    //根据id查找下标
    let index = list.findIndex(item => item.id === id)
    //根据下标修改checked属性
    list[index].checked = e.target.checked

    //设置状态
    this.setState({
      list
    })
  }

  //删除
  handleDelete(id) {
    let list = this.state.list
    //找下标
    let index = list.findIndex(item => item.id === id)
    //删除
    list.splice(index, 1)

    //更新状态
    this.setState({
      list
    })
  }

  componentDidUpdate() {
    console.log('更新完')
    let list = this.state.list
    localStorage.setItem('list', JSON.stringify(list))
  }

  componentDidMount() {
    console.log('挂载完')
    let list = JSON.parse(localStorage.getItem('list')) || []
    this.setState({
      list
    })
  }

  render() {
    return (
      <div>
        <Header onAdd={(title) => this.handleAdd(title)}></Header>
        <List 
          title="正在进行" 
          checked={false} 
          list={this.state.list} 
          onCheck={(e, id) => this.handleCheck(e, id)}
          onDelete={(id) => this.handleDelete(id)}></List>
        <List 
          title="已经完成" 
          checked={true} 
          list={this.state.list} 
          onCheck={(e, id) => this.handleCheck(e, id)}
          onDelete={(id) => this.handleDelete(id)}></List>        
      </div>
    )
  }
}

3.子组件

Header.js:

import React, { Component } from 'react'

export default class Header extends Component {
  state = {
    title: ''
  }

  //受控组件回调函数
  handleInput(e) {
    this.setState({
      title: e.target.value
    })
  }

  //监听回车事件
  handleEnter(e) {
    //点击回车按钮是keyCode=13,13是回车键的ASCII码
    if (e.keyCode === 13) {
      //子组件和父组件通讯
      this.props.onAdd(this.state.title)
      this.setState({
        title: ''
      })
    }
  }

  render() {
    return (
      <div>
        <input 
          value={this.state.title} 
          onChange={(e) => this.handleInput(e)} 
          onKeyUp={(e) => this.handleEnter(e)}
          placeholder="请输入要做的事情"></input>
      </div>
    )
  }
}

List.js:

import React, { Component } from 'react'

export default class List extends Component {
  render() {
    //过滤list数组
    let list = this.props.list.filter(item => item.checked === this.props.checked)

    return (
      <div>
        <div>{this.props.title + "(" + list.length + ')'}</div>
        <div>
          {
            //渲染列表
            list.map(item => {
              return (
                <div key={item.id} className="m-list-item">
                  <label className="m-list-info">
                    <input checked={item.checked} onChange={(e) => this.props.onCheck(e, item.id)} type="checkbox"></input>
                    {item.title}
                  </label>
                  <button className="m-delete" onClick={() => this.props.onDelete(item.id)}>删除</button>
                </div>
              )
            })
          }
        </div>
      </div>
    )
  }
}

4.效果展示

在这里插入图片描述

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.练习todolist
3.预习投掷骰子游戏

十一、骰子游戏

课程目标

  1. 用到知识点有哪些
  2. 父组件
  3. 子组件
  4. 效果展示

知识点

1.用到知识点有哪些

组件划分。
父子组件传值。
随机函数Math.random()。
Promise()。
考察学生的逻辑思维能力,运用所学的知识分析问题,解决问题。

2.父组件

核心代码在父组件。

import React, { Component } from 'react'
import Header from './components/Header'
import Dice from './components/Dice'
import Money from './components/Money'
import Footer from './components/Footer'

export default class App extends Component {
  state = {
    score: 10000,
    money: 0,
    guess: 0,  //0:小, 1:大, 2:豹子
    diceArr: []
  }

  //累加金额
  handleAdd(money) {
    this.setState({
      money: this.state.money + money
    })
  }

  //清空金额
  handleClear() {
    this.setState({
      money: 0
    })
  }

  //开始投掷骰子
  async handleStart(guess) {
    console.log('开始:' + guess)

    let arr = []
    await new Promise((resolve) => {
      let count = 0
      let timer = setInterval(() => {
        arr = []
        for (let i = 0; i < 3; i++) {
          arr.push(Math.floor(Math.random() * 6 + 1))
        }
        this.setState({
          diceArr: arr
        })
        count++
        if (count === 20) {
          clearInterval(timer)
          resolve(true)
        }
      }, 50)
    })

    //求和,也可以用for循环
    let sum = arr.reduce((total, item) => { 
      return total + item 
    }, 0)

    console.log(sum)
    let isWin = false
    //赌小,赢了
    if (sum <= 9 && guess === 0) {
      isWin = true
    } else if (sum >= 10 && guess === 1) { //赌大,赢了
      isWin = true
    }

    //赌豹子赢了
    if (arr[0] === arr[1] && arr[1] === arr[2] && guess === 2) {
      isWin = true
    }
    console.log(isWin)

    //赢了
    if (isWin) {
      if (guess !== 2) {
        this.setState({
          score: this.state.score + this.state.money
        })
      } else {
        this.setState({
          score: this.state.score + this.state.money * 24
        })
      }

    } else {  //输了
      this.setState({
        score: this.state.score - this.state.money
      })
    }
  }

  render() {
    return (
      <div className="m-wrap">
        <Header score={this.state.score}></Header>
        <Dice diceArr={this.state.diceArr}></Dice>
        <Money 
          money={this.state.money} 
          onAdd={(money) => this.handleAdd(money)}
          onClear={() => this.handleClear()}></Money>
        <Footer onStart={(guess) => this.handleStart(guess)}></Footer>
      </div>
    )
  }
}

3.子组件

Header.js:

import React, { Component } from 'react'

export default class Header extends Component {
  render() {
    return (
      <div className="m-header">
        {this.props.score}
      </div>
    )
  }
}

Dice.js:

import React, { Component } from 'react'

export default class Dice extends Component {
  render() {
    return (
      <div className="m-dice">
        {
          this.props.diceArr.map((item, index) => {
            return (
              <div key={index} className="m-dice-item">{item}</div>
            )
          }) 
        }
      </div>
    )
  }
}

Money.js:

import React, { Component } from 'react'

export default class Money extends Component {
  render() {
    return (
      <div className="m-money">
        <div className="m-left">
          <button onClick={() => this.props.onAdd(50)}>50</button>
          <button onClick={() => this.props.onAdd(100)}>100</button>
          <button onClick={() => this.props.onAdd(200)}>200</button>
          <button onClick={() => this.props.onAdd(500)}>500</button>
        </div>
        <div className="m-right">
          <button onClick={() => this.props.onClear()}>{this.props.money}</button>
        </div>
      </div>
    )
  }
}

Footer.js:

import React, { Component } from 'react'

export default class Footer extends Component {
  render() {
    return (
      <div className="m-footer">
        <button onClick={() => this.props.onStart(1)}></button>
        <button onClick={() => this.props.onStart(2)}>豹子</button>
        <button onClick={() => this.props.onStart(0)}></button>
      </div>
    )
  }
}


4.效果展示

在这里插入图片描述

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.练习骰子游戏
3.预习路由

十二、路由

课程目标

  1. 什么是路由
  2. 装包
  3. 常用的路由组件和方法
  4. 官网

知识点

1.什么是路由

路由这个概念由后端而来,后端的接口url也叫路由。后来前后端分离了,前端功能越来越强大,有了单页面应用这个概念。前端使用浏览器history api或者hash实现了前端路由,效果就是不依赖后端前端自己实现在单页面中实现多页面的效果。

2.装包

yarn add react-router-dom

3.常用的路由组件和方法

组件:
BrowserRouter、Switch、Route、Redirect、NavLink

方法:
withRouter(),this.props.history.push()

4.官网

https://reacttraining.com/react-router/web/guides/quick-start

授课思路

在这里插入图片描述

案例作业

1.上网阅读相关质料
2.练习路由配置
3.预习登陆(结合路由跳转)

posted @ 2020-05-16 10:20  徐同保  阅读(20)  评论(0编辑  收藏  举报