React教程

 

尚硅谷React教程

(01_尚硅谷_react简介&)

01_尚硅谷_react简介&

02_尚硅谷_react的基本使用

(DOM Rendering&Babel)

DOM Rendering&Babel

Babel支持jsx扩展语法,转为js代码

谷歌浏览器扩展文件

<body>
  <div id="test"></div>

  <script type="text/javascript" src="../js/react.development.js"></script>
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <script type="text/javascript" src="../js/babel.min.js"></script>
  <script type="text/babel"> //必须声明babel
    // 创建虚拟DOM元素
    const vDom = <h1>Hello React</h1> // 千万不要加引号
    // 渲染虚拟DOM到页面真实DOM容器中
    ReactDOM.render(vDom, document.getElementById('test'))
  </script>
</body>

03_尚硅谷_jsx理解和基本使用

(React的JSX语言&toLowerCase函数&createElement用法&实战:使用Render函数开发可排序的表格组件&使用代替模板功能)

React的JSX语言&toLowerCase函数&createElement用法&实战:使用Render函数开发可排序的表格组件&使用代替模板功能

大括号动态的值传入进去,不是简单文本

  <script type="text/javascript" src="../js/react.development.js"></script>
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <script type="text/javascript" src="../js/babel.min.js"></script>
  <script type="text/javascript">
    // 1. 创建虚拟DOM
    /*方式一: 纯JS(一般不用)创建虚拟DOM元素对象*/
    const msg = 'I Like You!'
    const myId = 'Atguigu'

    const vDOM1 = React.createElement('h2', {id: myId}, msg.toUpperCase())
    // 2. 渲染到真实页面
    const domContainer = document.getElementById('test1')
    // debugger
    ReactDOM.render(vDOM1, domContainer)
  </script>

  <script type="text/babel">
    // 1. 创建虚拟DOM
    /*方式二: JSX创建虚拟DOM元素对象*/
    const vDOM2 = <h3 id={myId.toUpperCase()}>{msg.toLowerCase()}</h3>
    // debugger
    // 2. 渲染到真实页面
    ReactDOM.render(vDOM2, document.getElementById('test2'))
  </script>

04_尚硅谷_jsx练习

(map&JSX实战之ReactJS)

map&JSX实战之ReactJS

把标签和js混在一起,

  <script type="text/babel">
    /*
     功能: 动态展示列表数据
     */
    /*
     技术点:
     1). 使用JSX创建虚拟DOM
     2). React能自动遍历显示数组中所有的元素
     3). array.map()的使用
     */

    // 数据的数组
    var names = ['jquery', 'zeptoo', 'angular', 'react全家桶', 'vue全家桶']

    // 数据的数组-->标签的数组
    var lis = []
    names.forEach((name, index) => lis.push(<li key={index}>{name}</li>))
    // 创建虚拟DOM
    const ul = <ul>{lis}</ul>
    // 渲染虚拟DOM
    ReactDOM.render(ul, document.getElementById('example1'))
    const ul2 = <ul>{
      names.map((name, index) => <li key={index}>{name}</li>)
    }</ul>
    ReactDOM.render(ul2, document.getElementById('example2'))
  </script>

05_尚硅谷_模块与组件的理解

( ES6模块的循环加载&Redux 组件)

ES6模块的循环加载&Redux 组件

06_尚硅谷_react组件的基本定义和使用

(把数据添加到组件中&加载组件&产品分类组件&React-Redux的should-ComponentUpdate实现)

把数据添加到组件中&加载组件&产品分类组件&React-Redux的should-ComponentUpdate实现

最终渲染<MyCompone/> 方法一定要return,没有直接调用函数,渲染时调用函数。

只有实例能调用方法,

  <script type="text/babel">
    // 1. 定义组件
    /*方式1: 工厂函数组件(简单组件)*/
    function MyComponent () {
      return <h2>工厂函数组件(简单组件)</h2>
    }
    /*方式2: ES6类组件(复杂组件)*/
    class MyComponent2 extends React.Component {
      render () {
        console.log(this) // MyComponent2的实例对象
        return <h2>ES6类组件(复杂组件)</h2>
      }
    }
    // 2. 渲染组件标签
    ReactDOM.render(<MyComponent />, document.getElementById('example1'))
    ReactDOM.render(<MyComponent2 />, document.getElementById('example2'))
  </script>

07_尚硅谷_组件三大属性(1)_state

(组件的生命周期&state&带状态的文本框组件&关于this对象&使用bind()方法绑定事件)

组件的生命周期&state&带状态的文本框组件&关于this对象&使用bind()方法绑定事件

this是组件对象

不用直接操作dom,this是组件对象

Props参数,this.state读取状态,解构赋值不用点

onClick事件监听,this是组件对象,调用组件对象的方法,setState是固定得到方法设置新的状态对象。左右相同名字可以省略

重写组件类render方法,新增handleClick内部this默认是undefined,让this要指向组件对象。

点击的时候是通过bind产生新的handleClick函数,只是函数体一模一样

也可以在onClick中 绑定,onclick绑定的是band产生的新的函数,这种写法效率低重写渲染。

<script type="text/babel">
  /*
  需求: 自定义组件, 功能说明如下
    1. 显示h2标题, 初始文本为: 你喜欢我
    2. 点击标题更新为: 我喜欢你
  */
  class Like extends React.Component {
    constructor (props) {
      super(props)
      // 初始化状态
      this.state = {
        isLikeMe: true
      }
      // 绑定this为组件对象
      this.change = this.change.bind(this)
    }
    change () {
      // 更新状态: this.setState()
      // this.state.isLikeMe = !this.state.isLikeMe // 不能更新更新某个状态
      this.setState({
        isLikeMe: !this.state.isLikeMe
      })
    }
    render () {
      console.log('render()')
      const text = this.state.isLikeMe ? '你喜欢我' : '我喜欢你'
      return <h2 onClick={this.change}>{text}</h2>
    }
  }
  ReactDOM.render(<Like />, document.getElementById('example'))
</script>

08_尚硅谷_组件三大属性(2)_props

(React的props和state&getDefaultProps &propTypes)

React的props和state&getDefaultProps &propTypes

组件有state状态不能使用工厂函数模式,通过标签属性传递属性。

标签属性名要和props后面的属性值一致

“”是字符串{}是数字18

传递多个属性,三点运算符

组件对象自带props,一旦传递了数据会自动渲染

三点运算符...

<script type="text/babel">

  /*
需求: 自定义用来显示一个人员信息的组件, 效果如页面. 说明
  1). 如果性别没有指定, 默认为男
  2). 如果年龄没有指定, 默认为18
  */

  //1. 定义组件类
  class Person extends React.Component {
    render() {
      console.log(this)
      return (
        <ul>
          <li>姓名: {this.props.name}</li>
          <li>性别: {this.props.sex}</li>
          <li>年龄: {this.props.age}</li>
        </ul>
      )
    }
  }
  // 对标签属性进行限制
  Person.propTypes = {
    name: PropTypes.string.isRequired,
    sex: PropTypes.string,
    age: PropTypes.number
  }
  // 指定属性的默认值
  Person.defaultProps = {
    sex: '男',
    age: 18
  }

  //2. 渲染组件标签
  const person = {
    name: 'Tom',
    sex: '女',
    age: 18
  }
  ReactDOM.render(<Person {...person}/>, document.getElementById('example1'))
  const person2 = {
    myName: 'JACK',
    age: 17
  }
  ReactDOM.render(<Person name={person2.myName} age={person2.age}/>,
    document.getElementById('example2'))
</script>

09_尚硅谷_组件三大属性(3)_refs和事件处理

(组件三大属性(3)_refs和事件处理&)

组件三大属性(3)_refs和事件处理&

不能getElementById获取input输入框,ref是input的标识

标识ref名称content是对象的属性名

Ref后面跟着回调函数,第一次渲染时执行,把当前的input绑定到组件对象this上,直接通过this获取input输入框

处理事件,所有的方法都需要bind万无一失,操作dom元素是发生事件的元素event

ref是一个回调函数,input当前的input绑定到this组件对象上

  <script type="text/babel">
    /*
    需求: 自定义组件, 功能说明如下:
      1. 界面如果页面所示
      2. 点击按钮, 提示第一个输入框中的值
      3. 当第2个输入框失去焦点时, 提示这个输入框中的值
   */
    //定义组件
    class MyComponent extends React.Component {
      constructor(props) {
        super(props) // 调用父类(Component)的构造函数
        //console.log(this)
        // 将自定义的函数强制绑定为组件对象
        this.handleClick = this.handleClick.bind(this) // 将返回函数中的this强制绑定为指定的对象, 并没有改变原来的函数中的this
      }
      // 自定义的方法中的this默认为null
      handleClick () {
        // alert(this) //this默认是null, 而不是组件对象
        // 得到绑定在当前组件对象上的input的值
        alert(this.msgInput.value)
      }
      handleBlur (event) {
        alert(event.target.value)
      }
      render () {
        return (
          <div>
            <input type="text" ref={input => this.msgInput = input}/>{' '}
            <button onClick={this.handleClick}>提示输入数据</button>{' '}
            <input type="text" placeholder="失去焦点提示数据" onBlur={this.handleBlur}/>
          </div>
        )
      }
    }
    // 渲染组件标签
    ReactDOM.render(<MyComponent />, document.getElementById('example'))
  </script>

10_尚硅谷_组件组合使用_初始化显示

(组件组合使用_初始化显示&)

组件组合使用_初始化显示&

状态为组件内部的状态,state是变化的,props是组件外部传入的属性,一个组件要显示数据可以由内部维护的数据state,一种是外部标签传入的数据,改变状态必须用一个方法setState。

复杂页面开发流程,外面根组件,列表组件

Add和List是App的子组件,只用渲染App组件即可。

组件化编码实现静态页面

只能有一个div根标签,

实现数据的动态化显示,数据保存在哪个组件内,数据在哪些组件要使用。两个组件都需要使用,放在App共同父组件中。

初始化要交给list显示,动态{}大括号传递数据,this.state读状态

<ul>里先来一个大括号,需要一个标签数组需要map方法,

解构赋值

<Add/>组件要传递一个长度数据传递给Add,重复提出来

数据在props属性里面,不在状态里

11_尚硅谷_组件组合使用_交互

(组件组合使用_交互&)

组件组合使用_交互&

一旦有大括号必须有return

一旦有一个新的函数,需要在构造器中定义

子组件改变父组件的状态,setState,状态在哪个组件,更新状态的行为就应该定义在哪个组件里。

传递todo参数,不能直接更新状态,必须通过setState更新,否则无法更新界面。

函数也是数据,可以当做一般数据传递函数,Add组件需要直接传递过去,名字一致不易出错。

不写this当前作用域找,this在组件对象中找

声明接收新的属性,接收addTodo字符串,传过来的属性在props中

This不对,一定要绑定this否则undefined,清除输入数据。只要自己定义的方法必须要加bind

子组件改变父组件的状态,状态在哪个组件,更新状态的行为就应该在哪个组件

  <script type="text/javascript" src="../js/react.development.js"></script>
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <script type="text/javascript" src="../js/prop-types.js"></script>
  <script type="text/javascript" src="../js/babel.min.js"></script>
  <script type="text/babel">
    /*
    1)拆分组件: 拆分界面,抽取组件
    2)实现静态组件: 使用组件实现静态页面效果
    3)实现动态组件
        ① 动态显示初始化数据
        ② 交互功能(从绑定事件监听开始)
     */
    // 应用组件
    class App extends React.Component {
      constructor (props) {
        super(props)
        // 初始化状态
        this.state = {
          todos: ['吃饭', '睡觉', '打豆豆']
        }
        this.add = this.add.bind(this)
      }
      add (todo) {
        const {todos} = this.state
        todos.unshift(todo)
        //更新状态
        this.setState({todos})
      }
      render () {
        const {todos} = this.state
        return (
          <div>
            <TodoAdd add={this.add} count={todos.length} />
            <TodoList todos={todos} />
          </div>
        )
      }
    }

    // 添加todo组件
    class TodoAdd extends React.Component {
      constructor (props) {
        super(props)
        this.addTodo = this.addTodo.bind(this)
      }
      addTodo () {
        // 读取输入数据
        const text = this.input.value.trim()
        // 查检
        if(!text) {
          return
        }
        // 保存到todos
        this.props.add(text)
        // 清除输入
        this.input.value = ''
      }
      render () {
        return (
          <div>
            <h2>Simple TODO List</h2>
            <input type="text" ref={input => this.input=input}/>
            <button onClick={this.addTodo}>Add #{this.props.count}</button>
          </div>
        )
      }
    }
    TodoAdd.propTypes = {
      add: PropTypes.func.isRequired,
      count: PropTypes.number.isRequired
    }

    // todo列表组件
    class TodoList extends React.Component {
      render () {
        const {todos} = this.props
        return (
          <ul>
            {
              todos.map((todo, index) => <li key={index}>{todo}</li>)
            }
          </ul>
        )
      }
    }
    TodoList.propTypes = {
      todos: PropTypes.array.isRequired
    }

    // 渲染应用组件标签
    ReactDOM.render(<App />, document.getElementById('example'))

  </script>

12_尚硅谷_组件组合使用_总结

(组件组合使用_总结&)

组件组合使用_总结&

13_尚硅谷_组件_收集表单数据

(组件_收集表单数据&)

组件_收集表单数据&

交互现做绑定事件,

构造器中初始化状态值,输入进去没有变又回到以前数据

原生dom的onChange,setState的名字要和state中的名字pwd一样

不用再去dom中取值,不用大括号,大括号点后面的要删除省略了

受控组件直接读状态state,自动收集数据,非受控自己获取input值

非受控手动去读输入框读取数据会直接操作DOM,受控直接读state状态自动会收集状态

<script type="text/babel">
  /*
  1. 问题: 在react应用中, 如何收集表单输入数据
  2. 包含表单的组件分类
    受控组件
    非受控组件
  */
  /*
  需求: 自定义包含表单的组件
    1. 界面如下所示
    2. 输入用户名密码后, 点击登陆提示输入信息
    3. 不提交表单
  */
  class LoginForm extends React.Component {
    constructor(props) {
      super(props)
      this.state = {
        username: ''
      }
      this.handleSubmit = this.handleSubmit.bind(this)
      this.handleChange = this.handleChange.bind(this)
    }

    handleChange(event) {
      this.setState({username: event.target.value})
    }

    handleSubmit(event) {
      alert(`准备提交的用户名为: ${this.state.username}, 密码:${this.pwdInput.value}`)

      // 阻止事件的默认行为: 提交表单
      event.preventDefault()
    }
    render () {

      return (
        <form onSubmit={this.handleSubmit} action="/test">
          <label>
            用户名:
            <input type="text" value={this.state.username} onChange={this.handleChange} />
          </label>&nbsp;
          <label>
            密码:
            <input type="password" ref={(input) => this.pwdInput = input} />
          </label>&nbsp;
          <input type="submit" value="登陆" />
        </form>
      )
    }
  }
  
  ReactDOM.render(<LoginForm />, document.getElementById('example'))
</script>

14_尚硅谷_组件_生命周期

(组件_生命周期&)

组件_生命周期&

传递文本msg,通过props显示

样式是键值对,外围的大括号代码里面要写js代码,里面的大括号代码我是一个js对象,冒号前是样式名,后面是值。

定时器渲染组件,组件的生命周期,初始化的时候启动定时器,只用启动一次即可。

Render第一次渲染显示,

最终要更新状态,this是函数里面的this,是内置声明周期的函数,相当于重写。

交给setInterval的不是function而是bind产生的新的函数。

小数加减法不是刚好为0

移除组件

组件移除时回调

<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
  /*
  需求: 自定义组件
    1. 让指定的文本做显示/隐藏的动画
    2. 切换时间为2S
    3. 点击按钮从界面中移除组件界面
   */
  class Fade extends React.Component {

    constructor (props) {
      super(props)
      console.log('constructor(): 创建组件对象')
      this.state = {
        opacity: 1
      }
      this.removeComponent = this.removeComponent.bind(this)
    }

    componentWillMount () {
      console.log('componentWillMount(): 初始化将要挂载')
    }
    // 组件渲染后调用
    componentDidMount () {// 在此方法中启动定时器/绑定监听/发送ajax请求
      console.log('componentDidMount(): 初始化已经挂载')
      // 保存到当前组件对象中
      this.intervalId = setInterval(function () {
        console.log('--------')
        // 得到当前opacity
        let {opacity} = this.state
        // 更新opacity
        opacity -= 0.1
        if(opacity<=0) {
          opacity = 1
        }
        // 更新状态
        this.setState({opacity})
      }.bind(this), 200)
    }

    componentWillUpdate () {
      console.log('componentWillUpdate(): 将要更新')
    }
    componentDidUpdate () {
      console.log('componentDidUpdate(): 已经更新')
    }

    componentWillUnmount () {// 清除定时器/解除监听
      console.log('componentWillUnmount(): 将要被移除')
      clearInterval(this.intervalId)
    }

    removeComponent () {
      ReactDOM.unmountComponentAtNode(document.getElementById('example'))
    }

    render() {
      console.log('render() 渲染组件')
      return (
        <div>
          <h2 style={{opacity:this.state.opacity}}>{this.props.content}</h2>
          <button onClick={this.removeComponent}>不活了</button>
        </div>
      )
    }
  }
  ReactDOM.render(<Fade content="react学不会, 怎么办?"/>, document.getElementById('example'))
</script>

15_尚硅谷_组件_生命周期总结

(组件_生命周期总结&)

组件_生命周期总结&

16_尚硅谷_虚拟DOM和DOM diff算法

(虚拟DOM和DOM diff算法&)

虚拟DOM和DOM diff算法&

17_尚硅谷_react脚手架应用分析

(react脚手架应用分析&)

react脚手架应用分析&

 

18_尚硅谷_使用react脚手架创建应用

(使用react脚手架创建应用&)

使用react脚手架创建应用&

 

19_尚硅谷_基于脚手架项目编写应用

(基于脚手架项目编写应用&)

基于脚手架项目编写应用&

所有组件都放在components文件夹里,组件是jsx文件

要暴露组件,默认暴露引用不用加大括号,方便

写一条语句

引入图片大括号

入口文件index.js,引入React和渲染ReactDOM,最终要渲染App组件标签,可省略后缀

对应用中所有模块编译打包运行,

引入样式模块,自定义模块必须以 ./或者../开头

20_尚硅谷_练习1_评论管理_拆分组件与实现静态组件

(练习1_评论管理_拆分组件与实现静态组件&)

练习1_评论管理_拆分组件与实现静态组件&

div拷贝到app.jsx中

必须要有结束标签

修改静态文件的class为className

Style必须两个大括号,js代码要有引用不然是变量了

拆分为多个组件中

组件文件夹下创建一个css

21_尚硅谷_练习1_评论管理_初始化数据动态显示

(练习1_评论管理_初始化数据动态显示&)

练习1_评论管理_初始化数据动态显示&

评论多个组件需要放在父组件上面,App是组件类,组件对象是this,不用写constructor

把属性传递过去,最好前面定义

传给commentList,读取属性前先得声明接收什么属性

下载--save运行时依赖,

给组件对象添加

给组件类添加

从属性里取东西,先取出来再说

初始化数据设置好属性名

在list中使用item组件

在大括号里写js代码,item已经声明好了名称必须使用comment

能一样名字尽量一样,不犯错误,{}括号里的要与形参一样

22_尚硅谷_练习1_评论管理_添加

(练习1_评论管理_添加&)

练习1_评论管理_添加&

交互,箭头函数没有自己的this,外围全局的this刚好是组件对象

绑定onChange监听,

读到里面的值,更新状态,setState和state名字一致。

This.sate是app.js父组件的属性

添加comment必须传递comment才能添加

调用addComment首先要声明

更新状态页面输入框状态就会发生改变

23_尚硅谷_练习1_评论管理_删除

(练习1_评论管理_删除&)

练习1_评论管理_删除&

Splice增删改都可以实现,删除index下标的元素

函数传递给list

props最好先从props中取出来,

删除传递index

计算是否显示

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <title>React App</title>
    <link rel="stylesheet" href="/css/bootstrap.css">
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
  </body>
</html>
import React from 'react'
import CommentAdd from '../comment-add/comment-add'
import CommentList from '../comment-list/comment-list'

class App extends React.Component {

  constructor (props) {
    super(props)

    this.state = {
      comments: []
    }

    this.delete = this.delete.bind(this)
  }

  componentDidMount () {
    //模拟异步获取数据
    setTimeout(() => {
      const comments = [
        {
          username: "Tom",
          content: "ReactJS好难啊!",
          id: Date.now()
        },
        {
          username: "JACK",
          content: "ReactJS还不错!",
          id: Date.now() + 1
        }
      ]
      this.setState({
        comments
      })
    }, 1000)
  }

  add = (comment) => {
    let comments = this.state.comments
    comments.unshift(comment)
    this.setState({ comments })
  }

  delete (index) {
    let comments = this.state.comments
    comments.splice(index, 1)
    this.setState({ comments })
  }

  render () {
    return (
      <div>
        <header className="site-header jumbotron">
          <div className="container">
            <div className="row">
              <div className="col-xs-12">
                <h1>请发表对React的评论</h1>
              </div>
            </div>
          </div>
        </header>
        <div className="container">
          <CommentAdd add={this.add}/>
          <CommentList comments={this.state.comments} delete={this.delete}/>
        </div>
      </div>
    )
  }
}

export default App
import React from 'react'
import PropTypes from 'prop-types'

class CommentAdd extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      username: '',
      content: ''
    }
    this.addComment = this.addComment.bind(this)
    this.changeUsername = this.changeUsername.bind(this)
    this.changeContent = this.changeContent.bind(this)
  }

  addComment () {
    // 根据输入的数据创建评论对象
    let { username, content } = this.state
    let comment = { username, content }
    // 添加到comments中, 更新state
    this.props.add(comment)
    // 清除输入的数据
    this.setState({
      username: '',
      content: ''
    })
  }

  changeUsername (event) {
    this.setState({
      username: event.target.value
    })
  }

  changeContent (event) {
    this.setState({
      content: event.target.value
    })
  }

  render () {
    return (
      <div className="col-md-4">
        <form className="form-horizontal">
          <div className="form-group">
            <label>用户名</label>
            <input type="text" className="form-control" placeholder="用户名"
                   value={this.state.username} onChange={this.changeUsername}/>
          </div>
          <div className="form-group">
            <label>评论内容</label>
            <textarea className="form-control" rows="6" placeholder="评论内容"
                      value={this.state.content} onChange={this.changeContent}></textarea>
          </div>
          <div className="form-group">
            <div className="col-sm-offset-2 col-sm-10">
              <button type="button" className="btn btn-default pull-right" onClick={this.addComment}>提交</button>
            </div>
          </div>
        </form>
      </div>
    )
  }
}
CommentAdd.propTypes = {
  add: PropTypes.func.isRequired
}

export default CommentAdd
import React from 'react'
import PropTypes from 'prop-types'
import CommentItem from '../comment-item/comment-item'
import './commentList.css'


class CommentList extends React.Component {
  constructor (props) {
    super(props)
  }

  render () {
    let comments = this.props.comments
    let display = comments.length > 0 ? 'none' : 'block'
    return (
      <div className="col-md-8">
        <h3 className="reply">评论回复:</h3>
        <h2 style={{ display: display }}>暂无评论,点击左侧添加评论!!!</h2>
        <ul className="list-group">
          {
            comments.map((comment, index) => {
              console.log(comment)
              return <CommentItem comment={comment} key={index} index={index} delete={this.props.delete}/>
            })
          }
        </ul>
      </div>
    )
  }
}
CommentList.propTypes = {
  comments: PropTypes.array.isRequired,
  delete: PropTypes.func.isRequired
}

export default CommentList
import React from 'react'
import PropTypes from 'prop-types'
import './commentItem.css'

class CommentItem extends React.Component {
  constructor (props) {
    super(props)
    this.deleteComment = this.deleteComment.bind(this)
  }

  deleteComment () {
    let username = this.props.comment.username
    if (window.confirm(`确定删除${username}的评论吗?`)) {
      this.props.delete(this.props.index)
    }
  }

  render () {
    let comment = this.props.comment
    return (
      <li className="list-group-item">
        <div className="handle">
          <a href="javascript:" onClick={this.deleteComment}>删除</a>
        </div>
        <p className="user"><span >{comment.username}</span><span>说:</span></p>
        <p className="centence">{comment.content}</p>
      </li>
    )
  }
}
CommentItem.propTypes = {
  comment: PropTypes.object.isRequired,
  index: PropTypes.number.isRequired,
  delete: PropTypes.func.isRequired
}

export default CommentItem

24_尚硅谷_ajax请求_使用axios

(ajax请求_使用axios&)

ajax请求_使用axios&

React项目发送Ajax请求

发送请求是异步的操作再初始化更新与死亡3阶段,尽快显示数据componentDidMount初始化一上来就发送ajax请求。

数据就在response里面,对象属性是固定的data

异常catch处理

25_尚硅谷_ajax请求_使用fetch

(ajax请求_使用fetch&)

ajax请求_使用fetch&

Fetch本身是promise风格尽量少嵌套,return返回的是一个promise对象,返回json对象

<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/axios/0.17.1/axios.min.js"></script>
<script type="text/babel">
  /*
  需求:
    1. 界面效果如下
    2. 根据指定的关键字在github上搜索匹配的最受关注的库
    3. 显示库名, 点击链接查看库
    4. 测试接口: https://api.github.com/search/repositories?q=r&sort=stars
  */

  class MostStarRepo extends React.Component {
    constructor (props) {
      super(props)
      this.state = {
        repoName: '',
        repoUrl: ''
      }
    }
    
    componentDidMount () {
      const url = `https://api.github.com/search/repositories?q=${this.props.searchWord}&sort=stars`
      // const url = `https://api.github.com/search/repositories2?q=${this.props.searchWord}&sort=stars`
      axios.get(url)
        .then(response => {
          const result = response.data
          console.log(result)
          const repo = result.items[0]
          this.setState({
            repoName: repo.name,
            repoUrl: repo.html_url
          })
        })
        .catch(error => {
          // debugger
          console.log(error)
          alert('请求失败 '+ error.message)
        })

      /*fetch(url, {method: "GET"})
        .then(response => response.json())
        .then(data => {
          console.log(data)
          if(data.message) {
            alert(`请求失败: ${data.message}`)
          } else {
            const repo = data.items[0]
            this.setState({
              repoName: repo.name,
              repoUrl: repo.html_url
            })
          }
        })*/
    }

    render () {
      const {repoName, repoUrl} = this.state
      if(!repoName) {
        return <h2>loading...</h2>
      } else {
        return (
          <h2>
            most star repo is <a href={repoUrl}>{repoName}</a>
          </h2>
        )
      }
    }
  }

  ReactDOM.render(<MostStarRepo searchWord="r"/>, document.getElementById('example'))
</script>

26_尚硅谷_练习2_用户搜索_初始化显示

(练习2_用户搜索_初始化显示&)

练习2_用户搜索_初始化显示&

不用洗px单位

App引入子组件

搜索有4个状态

Div有多个是个数组,

27_尚硅谷_练习2_用户搜索_交互

(练习2_用户搜索_交互&)

练习2_用户搜索_交互&

发请求获取对应的数据,在哪个组件发请求,发请求前后都要更新main的状态,所以在main组件发送请求。

点击button调用方法setSearch方法

调用方法出入input里面的内容,非受控组件ref,调用setSearch方法,出入searchName

点击搜索更新app.js中的state,状态需要让main知道,传递给main

Main.js根据不同searchName发送请求

数据没有变化需要更新状态,不报错的bug

知道接收到的属性发生了变化

新得到属性在参数中,当组件接收到新的属性时调用

最后要返回一个对象,大括号是一个函数体需要加一个小括号表示一个对象。

更新状态

兄弟组件search将searchName传递给List,点击搜索按钮告诉父亲组件更新state,由父亲组件通过属性传递给子组件,一旦searchName发送变化就会通知list重新渲染

import React from 'react'
import Search from './search'
import UserList from './user-list'

export default class App extends React.Component {

  state = {
    searchName: ''
  }

  refreshName = (searchName) => this.setState({searchName})

  render() {
    return (
      <div className="container">
        <section className="jumbotron">
          <h3 className="jumbotron-heading">Search Github Users</h3>
          <Search refreshName={this.refreshName}/>
        </section>
        <UserList searchName={this.state.searchName}/>
      </div>
    )
  }

}
/**
 * 上部的搜索模块
 */
import React, {Component} from 'react'
import PropTypes from 'prop-types'

class Search extends Component {

  static propTypes = {
    refreshName: PropTypes.func.isRequired
  }

  search = () => {
    var name = this.nameInput.value
    this.props.refreshName(name)
  }

  render() {
    return (
      <div>
        <input type="text" placeholder="enter the name you search"
               ref={(input => this.nameInput = input)}/>
        <button onClick={this.search}>Search</button>
      </div>
    )
  }
}

export default Search

list要知道接收到的属性发生了变化,通过componentWillReceiveProps组件将要接收到新的props执行

/**
 * 下部的用户列表模块
 */
import React from 'react'
import PropTypes from 'prop-types'
import axios from 'axios'

class UserList extends React.Component {

  static propTypes = {
    searchName: PropTypes.string.isRequired
  }

  state = {
    firstView: true,
    loading: false,
    users: null,
    error: null
  }

  async componentWillReceiveProps(nextProps)  {
    let searchName = nextProps.searchName
    console.log('发送ajax请求', searchName)
    const url = `https://api.github.com/search/users?q=${searchName}`
    this.setState({ firstView: false, loading: true })

    // 使用axios库
    axios.get(url)
      .then((response) => {
        console.log(response)
        this.setState({ loading: false, users: response.data.items })
      })
      .catch((error)=>{
        // debugger
        console.log('error', error.response.data.message, error.message)
        this.setState({ loading: false, error: error.message })
      })

    try {
      const result = await axios.get(url)
      this.setState({ loading: false, users: result.data.items })
    } catch(err) {
      // debugger
      console.log('----', err.message)
    }
  }

  render () {

    if (this.state.firstView) {
      return <h2>Enter name to search</h2>
    } else if (this.state.loading) {
      return <h2>Loading result...</h2>
    } else if (this.state.error) {
      return <h2>{this.state.error}</h2>
    } else {
      return (
        <div className="row">
          {
            this.state.users.map((user) => (
              <div className="card" key={user.html_url}>
                <a href={user.html_url} target="_blank">
                  <img src={user.avatar_url} style={{width: '100px'}} alt='user'/>
                </a>
                <p className="card-text">{user.login}</p>
              </div>
            ))
          }
        </div>
      )
    }
  }
}

export default UserList

28_尚硅谷_组件间通信的2种方式

(组件间通信的2种方式&)

组件间通信的2种方式&

兄弟之间通信还要借助父亲,search组件要发布消息通知所有关注的人。

订阅相当于绑定监听,要绑定事件名与回调函数,发布消息事件名与携带的数据

发送search名的消息,search发布消息

Main,comment-list要订阅消息,调用回调函数第一个参数消息名,第二个参数传递的数据根据实际接收数据的意义取名。

一旦回调函数都写箭头函数,回调函数的this肯定不是组件对象。

函数要不断地传递

去掉就没有必要声明,

发生事件的地方才需要发布消息,点击了发生了事件所以要发布消息。传递多个数据封装成对象。

comment-item发布消息

App里面要订阅消息,绑定监听需要在componentDidMount一上来就要绑定好

app.js订阅消息

29_尚硅谷_react-router说明

(react-router说明&)

react-router说明&

页面某些部分发生了变化,整个应用只有一个完整页面

前台路由就是点击显示一个页面

点击调用函数,return false不会发送http请求。

  <p><input type="text"></p>
  <a href="/test1" onclick="return push('/test1')">test1</a><br><br>
  <button onClick="push('/test2')">push test2</button><br><br>
  <button onClick="back()">回退</button><br><br>
  <button onClick="forword()">前进</button><br><br>
  <button onClick="replace('/test3')">replace test3</button><br><br>

  <script type="text/javascript" src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
  <script type="text/javascript">
    let history = History.createBrowserHistory() // 方式一
    history = History.createHashHistory() // 方式二
    // console.log(history)

    function push (to) {
      history.push(to)
      return false
    }

    function back() {
      history.goBack()
    }

    function forword() {
      history.goForward()
    }

    function replace (to) {
      history.replace(to)
    }

    history.listen((location) => {
      console.log('请求路由路径变化了', location)
    })
  </script>

30_尚硅谷_react-router基本使用

(react-router基本使用&)

react-router基本使用&

点击连接页面不刷新,路由组件

Web版本-dom

分为路由组件view与非路由组件两个文件夹

3个组件

用react-router不能直接渲染App要用一个东西包起立,管理整个应用

Router管理整个应用

两个导航连接不应该用a标签,

最终转化为a标签

只能显示其中一个路由组件

一上来空的

switch匹配才会显示,to和path要duiying

import React from 'react'
import {Route, Switch, Redirect} from 'react-router-dom'
import MyNavLink from './my-nav-link'
import About from '../views/about'
import Home from '../views/home'

export default class App extends React.Component {

  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header">
              <h2>React Router Demo</h2>
            </div>
          </div>
        </div>

        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              {/*导航路由链接*/}
              <MyNavLink className="list-group-item" to='/about'>About</MyNavLink>
              <MyNavLink className="list-group-item" to='/home'>Home</MyNavLink>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                {/*可切换的路由组件*/}
                <Switch>
                  <Route path='/about' component={About}/>
                  <Route path='/home' component={Home}/>
                  <Redirect to='/about'/>
                </Switch>
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}
import React from 'react'
export default function About() {
  return <div>About组件内容</div>
}
import React from 'react'
import {Switch, Route, Redirect} from 'react-router-dom'
import MyNavLink from '../components/my-nav-link'
import News from './news'
import Message from './message'

export default function Home() {
  return (
    <div>
      <h2>Home组件内容</h2>
      <div>
        <ul className="nav nav-tabs">
          <li>
            <MyNavLink to='/home/news'>News</MyNavLink>
          </li>
          <li>
            <MyNavLink to="/home/message">Message</MyNavLink>
          </li>
        </ul>
        <Switch>
          <Route path='/home/news' component={News} />
          <Route path='/home/message' component={Message} />
          <Redirect to='/home/news'/>
        </Switch>
      </div>
    </div>
  )
}
import React from 'react'

export default class News extends React.Component {
  state = {
    newsArr: ['news001', 'news002', 'news003']
  }

  render () {
    return (
      <div>
        <ul>
          {
            this.state.newsArr.map((news, index) => <li key={index}>{news}</li>)
          }
        </ul>
      </div>
    )
  }
}
import React from 'react'
import {Link, Route} from 'react-router-dom'
import MessageDetail from "./message-detail"

export default class Message extends React.Component {
  state = {
    messages: []
  }

  componentDidMount () {
    // 模拟发送ajax请求
    setTimeout(() => {
      const data = [
        {id: 1, title: 'Message001'},
        {id: 3, title: 'Message003'},
        {id: 6, title: 'Message006'},
      ]
      this.setState({
        messages: data
      })
    }, 1000)
  }

  ShowDetail = (id) => {
    this.props.history.push(`/home/message/${id}`)
  }

  ShowDetail2 = (id) => {
    this.props.history.replace(`/home/message/${id}`)
  }

  back = () => {
    this.props.history.goBack()
  }

  forward = () => {
    this.props.history.goForward()
  }

  render () {
    const path = this.props.match.path

    return (
      <div>
        <ul>
          {
            this.state.messages.map((m, index) => {
              return (
                <li key={index}>
                  <Link to={`${path}/${m.id}`}>{m.title}</Link>
                  &nbsp;&nbsp;&nbsp;
                  <button onClick={() => this.ShowDetail(m.id)}>查看详情(push)</button>&nbsp;
                  <button onClick={() => this.ShowDetail2(m.id)}>查看详情(replace)</button>
                </li>
              )
            })
          }
        </ul>
        <p>
          <button onClick={this.back}>返回</button>&nbsp;
          <button onClick={this.forward}>前进</button>&nbsp;
        </p>
        <hr/>
        <Route path={`${path}/:id`} component={MessageDetail}></Route>
      </div>
    )
  }
}
import React from 'react'

const messageDetails = [
  {id: 1, title: 'Message001', content: '我爱你, 中国'},
  {id: 3, title: 'Message003', content: '我爱你, 老婆'},
  {id: 6, title: 'Message006', content: '我爱你, 孩子'},
]

export default function MessageDetail(props) {

  const id = props.match.params.id
  const md = messageDetails.find(md => md.id===id*1)

  return (
    <ul>
      <li>ID: {md.id}</li>
      <li>TITLE: {md.title}</li>
      <li>CONTENT: {md.content}</li>
    </ul>
  )
}

31_尚硅谷_NavLink组件包装优化

(NavLink组件包装优化&)

NavLink组件包装优化&

传递属性到NavLink中,内部控制当前选中样式

import React from 'react'
import {NavLink} from 'react-router-dom'

export default function MyNavLink(props) {
  return <NavLink {...props} activeClassName='activeClass'/>
}

32_尚硅谷_嵌套路由

(嵌套路由&)

嵌套路由&

路由组件又包含了路由,二级路由

指定路径是home下面的news

模拟发送ajax请求更新状态,只要回调函数都用箭头函数,

Home组件中显示,路由链接to指定路径,在Route路由组件中展现只显示一种可能性

默认自动显示

33_尚硅谷_向路由组件传递数据

(向路由组件传递数据&)

向路由组件传递数据&

向组件传递数据props,不是标签没办法传递数据

点击不同的链接根据不同id查询内容

写js代码需要用大括号括起来,用反引号或者拼串方式动态拼接,:id占位符标识名称

请求路径和route匹配最终显示出来。

看哪个里面有id

用的是函数不是组件类的形式,参数接收props

Find方法返回第一个结果为true的元素,m就是find的结果。*1变为数字

34_尚硅谷_路由链接与非路由链接说明

(路由链接与非路由链接说明&)

路由链接与非路由链接说明&

路由链接不发送请求

路由链接必须用NavLink或者Route,替换a标签

35_尚硅谷_2种路由跳转的方式

(2种路由跳转的方式&)

2种路由跳转的方式&

不知道点击的是哪一个按钮,调用push方法传入id值,没有调用不能传递参数

Onclick函数点击执行,向回调函数里面传递参数

Replace查看,区别在于回退的时候效果不一样。

36_尚硅谷_react-ui_antd

(react-ui_antd&)

react-ui_antd&

放在index.html中

启动服务器和客户端

按需打包

编译打包的工具包

import React, {Component} from 'react'
// 分别引入需要使用的组件
// import Button from 'antd-mobile/lib/button'
// import Toast from 'antd-mobile/lib/toast'
import {Button, Toast} from 'antd-mobile'
export default class App extends Component {
  handleClick = () => {

    Toast.info('提交成功', 2)
  }

  render() {
    return (
      <div>
        <Button type="primary" onClick={this.handleClick}>提交</Button>
      </div>
    )
  }
}

37_尚硅谷_redux_理解

(redux_理解&)

redux_理解&

多个组件共享状态,状态在哪里修改状态的行为就定义在哪个组件里,集中式的管理状态。

单应用有很多组件,

通过store对象来存储状态,dispatch分发通知store更新状态流程

38_尚硅谷_redux_counter应用_react版本

(redux_counter应用_react版本&)

redux_counter应用_react版本&

动态组件先初始化状态显示,数据名称类型位置,变化的数据

绑定事件监听,使用箭头函数this才会是组件对象

import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'

import App from './components/app/app'
import store from './redux/store'

// 定义渲染根组件标签的函数
ReactDOM.render(
  (
    <Provider store={store}>
      <App/>
    </Provider>
  ),
  document.getElementById('root')
)
import React, {Component} from 'react'

export default class App extends Component {

  state = {
    count: 0
  }

  increment = () => {
    const num = this.refs.numSelect.value*1
    const count = this.state.count + num
    this.setState({count})
  }

  decrement = () => {
    const num = this.refs.numSelect.value*1
    const count = this.state.count - num
    this.setState({count})
  }

  incrementIfOdd = () => {
    let count = this.state.count
    if(count%2==1) {
      const num = this.refs.numSelect.value*1
      count += num
      this.setState({count})
    }
  }

  incrementAsync = () => {
    setTimeout(() => {
      const num = this.refs.numSelect.value*1
      const count = this.state.count + num
      this.setState({count})
    }, 1000)
  }

  render () {
    const {count} = this.state

    return (
      <div>
        <p>
          click {count} times {' '}
        </p>
        <select ref="numSelect">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>{' '}
        <button onClick={this.increment}>+</button>{' '}
        <button onClick={this.decrement}>-</button>{' '}
        <button onClick={this.incrementIfOdd}>increment if odd</button>{' '}
        <button onClick={this.incrementAsync}>increment async</button>
      </div>
    )
  }
}

39_尚硅谷_redux_counter应用_redux版本

(redux_counter应用_redux版本&)

redux_counter应用_redux版本&

下载依赖包

Reducer根据老状态获得一个新状态,

返回小括号大括号是一个对象

createStore接收reducer,不同状态有不同的函数(reducers)

引入reducer函数counter,store管理上reducer

一个模块只有一个default,不同type做的事情不一样swith代替if/else,return新的状态。

要在原来的基础上增加data值

不变化的常量值

状态state交给reducer管理,更新状态的操作setState交给reducer去做

把store传递给 App

Props中有store

不能有大括号,本来就是数值不能加大括号

更新状态的操作

传递表示不一样,要先得到count值

监听为了状态更新了需要重新绘制组件

reducers.js ,根据reducer产生新的state的函数

/*
根据老的state和指定action, 处理返回一个新的state
 */
import {INCREMENT, DECREMENT} from './action-types'

export function counter(state = 0, action) {
  console.log('counter', state, action)
  switch (action.type) {
    case INCREMENT:
      return state + action.number
    case DECREMENT:
      return state - action.number
    default:
      return state
  }
}
/*
action creator模块
 */
import {INCREMENT, DECREMENT} from './action-types'

export const increment = number => ({type: INCREMENT, number})
export const decrement = number => ({type: DECREMENT, number})
/*
Action对象的type常量名称模块
 */
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
import React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'

import App from './components/app'
import {counter} from './redux/reducers'

// 根据counter函数创建store对象
const store = createStore(counter)

// 定义渲染根组件标签的函数
const render = () => {
  ReactDOM.render(
    <App store={store}/>,
    document.getElementById('root')
  )
}
// 初始化渲染
render()

// 注册(订阅)监听, 一旦状态发生改变, 自动重新渲染
store.subscribe(render)

/*
应用组件
 */
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import * as actions from '../redux/actions'

export default class App extends Component {

  static propTypes = {
    store: PropTypes.object.isRequired,
  }

  increment = () => {
    const number = this.refs.numSelect.value * 1
    this.props.store.dispatch(actions.increment(number))
  }

  decrement = () => {
    const number = this.refs.numSelect.value * 1
    this.props.store.dispatch(actions.decrement(number))
  }

  incrementIfOdd = () => {
    const number = this.refs.numSelect.value * 1

    let count = this.props.store.getState()
    if (count % 2 === 1) {
      this.props.store.dispatch(actions.increment(number))
    }
  }

  incrementAsync = () => {
    const number = this.refs.numSelect.value * 1
    setTimeout(() => {
      this.props.store.dispatch(actions.increment(number))
    }, 1000)
  }

  render() {
    return (
      <div>
        <p>
          click {this.props.store.getState()} times {' '}
        </p>
        <select ref="numSelect">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>{' '}
        <button onClick={this.increment}>+</button>
        {' '}
        <button onClick={this.decrement}>-</button>
        {' '}
        <button onClick={this.incrementIfOdd}>increment if odd</button>
        {' '}
        <button onClick={this.incrementAsync}>increment async</button>
      </div>
    )
  }
}

41_尚硅谷_redux_counter应用_react-redux版本

(redux_counter应用_react-redux版本&)

redux_counter应用_react-redux版本&

React-redux降低耦合度

Provider包裹起来,代替subscribe

propTypes声明接收的属性

不依赖

Props有count解构赋值,纯react的代码没有redux

通过connect连接react的组件和redux,将两个组件关联起来,最终方法与数据状态都要从redux取,

不能直接暴露App,connect执行要接收一个组件类,接收一个新的组件,返回一个新的组件不是原来的组件。

函数返回值类型是对象,第一个参数count在state中存的,第二个参数传入的是两个函数,对象中的所有数据都会解构交给App组件,做为属性传递。

取状态应该是props.count一点redux代码都没有

整个应用进行全局的管理,

内部包装了3个组件

第一个是一般属性,后面两个是函数属性

属性都在Redux中,声明接收属性与方法,其他的都是一般的写法。

Count要和App中声明的属性名一致

等价,方法increment要和声明的propTypes属性名一致,作为App属性的属性名传递,后面黑色的属性值increment要和actions声明的方法一致。

最后要调用dispatch,变化的actions后面的

UI组件不适用redux的API,容器组件使用Redux的API

App没有用到Redux组件

返回的组件利用Redux相关的API生成的

不需要redux的API

包装Counter,把状态count变为了属性没有redux前自己管理属性,用来redux后接受属性

引入containers下面的app.jsx

状态操作写常量,

生成action对象的函数,没有业务

根据原来老的状态和action形成一个新的状态

Store固定写法

Provider要接收store,connect将组建与redux连接起来,目的向UI组件传递mapStat两个属性,mapStateToprops参数类型是函数

函数返回是一个对象,从state中读数据,没法写对象回调函数传入state

mapDispatchToProps参数类型是对象,包含一些从actions模块中得到的方法,转换成对于dispatch函数调动。

reducers.js

import {combineReducers} from 'redux'
import {
  INCREMENT,
  DECREMENT
} from './action-types'

function counter(state = 0, action) {
  console.log('counter', state, action)
  switch (action.type) {
    case INCREMENT:
      return state + action.number
    case DECREMENT:
      return state - action.number
    default:
      return state
  }
}

export default combineReducers({
  counter
})

actions.js

/*
action creator模块
 */
import {
  INCREMENT,
  DECREMENT
} from './action-types'

export const increment = number => ({type: INCREMENT, number})

export const decrement = number => ({type: DECREMENT, number})

// 异步action creator(返回一个函数)
export const incrementAsync = number => {
  return dispatch => {
    setTimeout(() => {
      dispatch(increment(number))
    }, 1000)
  }
}

counter.js

/*
包含Counter组件的容器组件
 */
import React from 'react'
import PropTypes from 'prop-types'

export default class Counter extends React.Component {

  static propTypes = {
    count: PropTypes.number.isRequired,
    increment: PropTypes.func.isRequired,
    decrement: PropTypes.func.isRequired
  }

  increment = () => {
    const number = this.refs.numSelect.value*1
    this.props.increment(number)
  }

  decrement = () => {
    const number = this.refs.numSelect.value*1
    this.props.decrement(number)
  }

  incrementIfOdd = () => {
    const number = this.refs.numSelect.value*1
    let count = this.props.count
    if(count%2===1) {
      this.props.increment(number)
    }
  }

  incrementAsync = () => {
    const number = this.refs.numSelect.value*1
    this.props.incrementAsync(number)
  }

  render () {
    return (
      <div>
        <p>
          click {this.props.count} times {' '}
        </p>
        <select ref="numSelect">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>{' '}
        <button onClick={this.increment}>+</button>{' '}
        <button onClick={this.decrement}>-</button>{' '}
        <button onClick={this.incrementIfOdd}>increment if odd</button>{' '}
        <button onClick={this.incrementAsync}>increment async</button>
      </div>
    )
  }
}

store.js

import React from 'react'
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'

import reducers from './reducers'

// 根据counter函数创建store对象
export default createStore(
  reducers,
  composeWithDevTools(applyMiddleware(thunk)) // 应用上异步中间件
)

app.jsx

/*
包含Counter组件的容器组件
 */
import React from 'react'
// 引入连接函数
import {connect} from 'react-redux'
// 引入action函数
import {increment, decrement} from '../redux/actions'

import Counter from '../components/counter'

// 向外暴露连接App组件的包装组件
export default connect(
  state => ({count: state}),
  {increment, decrement}
)(Counter)

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'

import App from './containers/app'
import store from './redux/store'

// 定义渲染根组件标签的函数
ReactDOM.render(
  (
    <Provider store={store}>
      <App/>
    </Provider>
  ),
  document.getElementById('root')
)


42_尚硅谷_redux_counter应用_redux异步版本

(redux_counter应用_redux异步版本&)

redux_counter应用_redux异步版本&

发Ajax请求,redux不支持异步操作,express通过中间件实现功能扩展当前库的插件

应用异步中间件,

异步在组件中实现的

过了一秒增加,函数参数是从容器组件传过来的incrementAsync

定义异步actions返回是一个函数,setTimeout里面触发更新,调用dispatch分发actions才会产生新的状态。

外面是一个函数,return的还是一个函数,在函数中才能执行异步代码,分发同步action

用上中间件才支持return函数

43_尚硅谷_redux_counter应用_使用redux调试工具

(redux_counter应用_使用redux调试工具&)

redux_counter应用_使用redux调试工具&

状态不在组件上了,

再包裹一层扩展插件

44_尚硅谷_redux_comment应用_redux版本_同步功能

(redux_comment应用_redux版本_同步功能&)

redux_comment应用_redux版本_同步功能&

数据异步从后台获取,

发请求获取数据,列表1s后显示出来

UI组件和容器组件,ui组件需要redux的API包装

App.js中的comments属性获取,异步的操作要放到actions中去做,统一更新的函数应该是属性中传入的。以前自身的state和函数由外部的redux传入

组件要用connect包裹一下,才能接受三个属性

返回对象要有属性名,reducer中的state就是一个数组。

对于属性和数据的操作,加和减是同步操作,点击立即生效

同步增加的action操作和删除的action操作,调用函数有没有传递参数

方法名与connect引入的一致

删除从下标开始删除

操作评论数组,以操作的数据命名,state要指定默认初始值,switch写法比较固定,default是在第一次的时候调用

返回新的状态也是一个数组,不能直接修改state,action.data是comment对象,后面原理状态在老状态新增加data。Action.data是一个下标状态要变了,要返回一个数组filter不改变以前数组,新数组比以前的数组少一些元素。

向外暴露store对象,通过createStore方法产生的。

向外暴露comments,必须要加大括号没有加defualt可能暴露多个。

45_尚硅谷_redux_comment应用_redux版本_异步功能

(redux_comment应用_redux版本_异步功能&)

redux_comment应用_redux版本_异步功能&

一上来应该是一个空数组,actions中增加异步获取数据的action,dispatch写异步代码模拟发送ajax请求,分发一个同步的action接收数据,action不能自己创建需要同步函数创建

有了新的action type,reducer要处理,接收新的状态数组,新数组成为了新的状态state

Receive_comments操作什么时候触发,receviceComments不用暴露

getComments函数要传递个App,getComments操作上来就要做声明调用

声明更加完善,异步代码要封装到actions中

上传代码没有异步代码

Reducer中有多个函数,分别暴露不太好

就管理一个comments函数

管理多个函数,combineReducers合并多个放在一起管理

把所有函数的export去掉,统一暴露,combineReducers也是一个函数结果,两个方法名,最好同名,不同名冒号。State是一个对象里面有对应的2和[]数组对应返回状态的结果

暴露对应属性上

Default暴露不能写大括号得到某一个函数,直接引入reducers

Reducers只能暴露它

最后App要变化,要管理两个state状态,对象包装起来,真实应用有多个状态会有多个reducer

action-types.js

export const ADD_COMMENT = 'ADD_COMMENT'
export const DELETE_COMMENT = 'DELETE_COMMENT'
export const RECEIVE_COMMENTS = 'RECEIVE_COMMENTS'

actions.js

import {
  ADD_COMMENT,
  DELETE_COMMENT,
  RECEIVE_COMMENTS
} from './action-types'

export const addComment = (comment) => ({type: ADD_COMMENT, data: comment})

export const deleteComment = (index) => ({type: DELETE_COMMENT, data: index})

const receiveComments = (comments) => ({type: RECEIVE_COMMENTS, data: comments})
export const getComments = () => {
  return dispatch => {
    setTimeout(() => {
      const comments = [
        {
          username: "Tom",
          content: "ReactJS好难啊!",
          id: Date.now()
        },
        {
          username: "JACK",
          content: "ReactJS还不错!",
          id: Date.now() + 1
        }
      ]
      dispatch(receiveComments(comments))
    }, 1000)
  }
}

reducers.js

import {combineReducers} from 'redux'

import {
  ADD_COMMENT,
  DELETE_COMMENT,
  RECEIVE_COMMENTS
} from './action-types'

const initComments = []

function comments(state = initComments, action) {
  switch (action.type) {
    case ADD_COMMENT:
      return [...state, action.data]
    case DELETE_COMMENT:
      return state.filter((c, index) => index !== action.data)
    case RECEIVE_COMMENTS:
      return action.data
    default:
      return state
  }
}

export default combineReducers({
  comments
})

store.js

import React from 'react'
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'

import reducers from './reducers'

// 根据counter函数创建store对象
export default createStore(
  reducers,
  composeWithDevTools(applyMiddleware(thunk)) // 应用上异步中间件
)

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'

import App from './components/app/app'
import store from './redux/store'

// 定义渲染根组件标签的函数
ReactDOM.render(
  (
    <Provider store={store}>
      <App/>
    </Provider>
  ),
  document.getElementById('root')
)

app.jsx

import React from 'react'
import {connect} from 'react-redux'
import CommentAdd from '../comment-add/comment-add'
import CommentList from '../comment-list/comment-list'
import {getComments} from '../../redux/actions'

class App extends React.Component {

  componentDidMount() {
    //模拟异步获取数据
    this.props.getComments()
  }

  render() {
    return (
      <div>
        <header className="site-header jumbotron">
          <div className="container">
            <div className="row">
              <div className="col-xs-12">
                <h1>请发表对React的评论</h1>
              </div>
            </div>
          </div>
        </header>
        <div className="container">
          <CommentAdd/>
          <CommentList/>
        </div>
      </div>
    )
  }
}

export default connect(
  null,
  {getComments}
)(App)

comment-list.jsx

import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import CommentItem from '../comment-item/comment-item'
import './commentList.css'


class CommentList extends React.Component {

  render () {
    let comments = this.props.comments
    let display = comments.length > 0 ? 'none' : 'block'
    return (
      <div className="col-md-8">
        <h3 className="reply">评论回复:</h3>
        <h2 style={{ display: display }}>暂无评论,点击左侧添加评论!!!</h2>
        <ul className="list-group">
          {
            comments.map((comment, index) => {
              console.log(comment)
              return <CommentItem comment={comment} key={index} index={index}/>
            })
          }
        </ul>
      </div>
    )
  }
}
CommentList.propTypes = {
  comments: PropTypes.array.isRequired,
}

export default connect(
  state => ({comments: state.comments})
)(CommentList)

comment-add.jsx

import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import {addComment} from '../../redux/actions'

class CommentAdd extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      username: '',
      content: ''
    }
    this.addComment = this.addComment.bind(this)
    this.changeUsername = this.changeUsername.bind(this)
    this.changeContent = this.changeContent.bind(this)
  }

  addComment () {
    // 根据输入的数据创建评论对象
    let { username, content } = this.state
    let comment = { username, content }
    // 添加到comments中, 更新state
    this.props.addComment(comment)
    // 清除输入的数据
    this.setState({
      username: '',
      content: ''
    })
  }

  changeUsername (event) {
    this.setState({
      username: event.target.value
    })
  }

  changeContent (event) {
    this.setState({
      content: event.target.value
    })
  }

  render () {
    return (
      <div className="col-md-4">
        <form className="form-horizontal">
          <div className="form-group">
            <label>用户名</label>
            <input type="text" className="form-control" placeholder="用户名"
                   value={this.state.username} onChange={this.changeUsername}/>
          </div>
          <div className="form-group">
            <label>评论内容</label>
            <textarea className="form-control" rows="6" placeholder="评论内容"
                      value={this.state.content} onChange={this.changeContent}></textarea>
          </div>
          <div className="form-group">
            <div className="col-sm-offset-2 col-sm-10">
              <button type="button" className="btn btn-default pull-right" onClick={this.addComment}>提交</button>
            </div>
          </div>
        </form>
      </div>
    )
  }
}
CommentAdd.propTypes = {
  addComment: PropTypes.func.isRequired
}

export default connect(
  null,
  {addComment}
)(CommentAdd)

comment-item.jsx

import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'

import './commentItem.css'
import {deleteComment} from '../../redux/actions'

class CommentItem extends React.Component {
  constructor(props) {
    super(props)
  }

  deleteComment = () => {
    let username = this.props.comment.username
    if (window.confirm(`确定删除${username}的评论吗?`)) {
      this.props.deleteComment(this.props.index)
    }
  }

  render() {
    let comment = this.props.comment
    return (
      <li className="list-group-item">
        <div className="handle">
          <a href="javascript:" onClick={this.deleteComment}>删除</a>
        </div>
        <p className="user"><span>{comment.username}</span><span>说:</span></p>
        <p className="centence">{comment.content}</p>
      </li>
    )
  }
}

CommentItem.propTypes = {
  comment: PropTypes.object.isRequired,
  index: PropTypes.number.isRequired,
  deleteComment: PropTypes.func.isRequired
}

export default connect(
  null,
  {deleteComment}
)(CommentItem)

 

 

 

 

 

 

### 回答1: React教程PDF是一种电子书,用于学习和了解React框架的使用和开发。这个教程通常包含基本概念、核心概念、组件、状态管理、路由等内容。 在这个教程中,你可以学习到如何使用React构建UI组件,React的虚拟DOM概念以及其在性能优化方面的作用。此外,还会介绍React的生命周期方法,这在处理组件的加载、渲染和更新过程中很有用。 除了React基本知识外,教程还可能包含一些实际应用方面的案例和实践经验。这些案例可以帮助你更好地理解React的使用,并提供一些编程技巧和最佳实践。 通常,React教程PDF是免费提供的,你可以从官方网站、开源社区或一些教育平台上找到这些资源。当然,有时也会出现一些收费的高质量教程,这些教程会提供更深入和全面的内容。 无论你是React初学者还是有一些经验的开发者,通过阅读React教程PDF可以帮助你扩展知识和提升技能,进一步了解React框架。最重要的是,你可以通过教程中提供的实例和指导来实践和运用你的学习成果,以便更好地应用React在真实项目中。 ### 回答2: React 是一个用于构建用户界面的 JavaScript 库,它可以帮助开发者创建高效、可重用的组件化界面。如果你想学习 React,最好的方式是通过官方提供的教程React 的官方网站提供了一个详尽的教程,其中包含了许多实用的示例和解释。这个教程是免费提供的,并且可以在网站上直接阅读或者下载为 PDF 格式。它的内容非常全面,适合初学者入门以及有一定经验的开发者用作参考。 在这个教程中,你将学习到 React 的基本概念,包括组件、状态和属性等。你将了解到如何创建组件、处理事件、进行条件渲染等等。教程还会引导你使用 JSX 语法编写代码,这是 React 的核心特性之一,可以让你在 JavaScript 中嵌入 XML 标记,更加直观地描述界面结构。 此外,教程还会介绍如何使用 React 的生态系统,比如 React Router 进行路由管理,以及 Redux 进行状态管理。你将了解到如何使用 Babel 和 Webpack 进行工程化的配置,以及如何使用 React 开发工具进行调试。 总的来说,React 的官方教程提供了非常丰富的内容,无论你是初学者还是有一定经验的开发者,都能从中获得很多收益。如果你想深入学习 React,我强烈推荐你阅读这个教程。你可以访问 React 官方网站并下载相应的 PDF 版本。祝你学习愉快! ### 回答3: React 是一个用于构建用户界面的 JavaScript 库。它提供了高效的设计和开发工具,使得前端开发变得更加简单和快捷。React 使用虚拟 DOM 的概念,将页面的更改抽象为不同的状态,在状态变化时只需要更新需要修改的部分,而不是整个页面,从而提高了性能。 React教程主要介绍了 React 的基本概念和用法。它从环境搭建开始,讲解了如何使用 React 的各个部分,包括组件、props、状态管理等。教程提供了一些示例代码,并对其进行了详细解释,让读者能够快速入门并理解 React 的基本原理。 React 教程的优点之一是它的实用性。通过教程,读者可以学习到如何通过 React 构建复杂的用户界面,并了解到一些常用的设计模式和最佳实践。教程还提供了一些实例项目,帮助读者将所学应用到实际项目中。 此外,教程还介绍了一些与 React 相关的工具和库,如 React Router 和 Redux。这些工具可以帮助开发者更好地组织和管理 React 项目,提高开发效率。 总的来说,React 教程是一个很好的入门资源,它能够帮助读者快速掌握 React 的基本概念和用法,并提供了一些实用的示例和项目,让读者能够在实际开发中更好地应用 React。无论是刚入门的前端开发者还是有一定经验的开发者,都可以从中获得很多有益的知识和经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wespten

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值