您当前的位置: 首页 > 

wespten

暂无认证

  • 1浏览

    0关注

    899博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

React教程

wespten 发布时间:2020-05-04 10:46:18 ,浏览量:1

 

尚硅谷React教程

(01_尚硅谷_react简介&)

01_尚硅谷_react简介&

02_尚硅谷_react的基本使用

(DOM Rendering&Babel)

DOM Rendering&Babel

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

谷歌浏览器扩展文件


  
//必须声明babel // 创建虚拟DOM元素 const vDom = Hello React // 千万不要加引号 // 渲染虚拟DOM到页面真实DOM容器中 ReactDOM.render(vDom, document.getElementById('test'))

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

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

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

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

  
  
  
  
    // 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)
  

  
    // 1. 创建虚拟DOM
    /*方式二: JSX创建虚拟DOM元素对象*/
    const vDOM2 = {msg.toLowerCase()}
    // debugger
    // 2. 渲染到真实页面
    ReactDOM.render(vDOM2, document.getElementById('test2'))
  

04_尚硅谷_jsx练习

(map&JSX实战之ReactJS)

map&JSX实战之ReactJS

把标签和js混在一起,

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

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

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

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

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

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

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

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

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

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

    只有实例能调用方法,

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

    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产生的新的函数,这种写法效率低重写渲染。

    
      /*
      需求: 自定义组件, 功能说明如下
        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 {text}
        }
      }
      ReactDOM.render(, document.getElementById('example'))
    

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

    (React的props和state&getDefaultProps &propTypes)

    React的props和state&getDefaultProps &propTypes

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

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

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

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

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

    三点运算符...

    
    
      /*
    需求: 自定义用来显示一个人员信息的组件, 效果如页面. 说明
      1). 如果性别没有指定, 默认为男
      2). 如果年龄没有指定, 默认为18
      */
    
      //1. 定义组件类
      class Person extends React.Component {
        render() {
          console.log(this)
          return (
            
    • 姓名: {this.props.name}
    • 性别: {this.props.sex}
    • 年龄: {this.props.age}
    ) } } // 对标签属性进行限制 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(, document.getElementById('example1')) const person2 = { myName: 'JACK', age: 17 } ReactDOM.render(, document.getElementById('example2'))

    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组件对象上

      
        /*
        需求: 自定义组件, 功能说明如下:
          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 (
              
    this.msgInput = input}/>{' '} 提示输入数据{' '}
    ) } } // 渲染组件标签 ReactDOM.render(, document.getElementById('example'))

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

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

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

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

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

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

    组件化编码实现静态页面

    只能有一个div根标签,

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

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

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

      解构赋值

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

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

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

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

      组件组合使用_交互&

      一旦有大括号必须有return

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

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

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

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

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

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

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

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

        
        
        
        
        
          /*
          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 (
                
      ) } } // 添加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 (
      Simple TODO List this.input=input}/> Add #{this.props.count}
      ) } } TodoAdd.propTypes = { add: PropTypes.func.isRequired, count: PropTypes.number.isRequired } // todo列表组件 class TodoList extends React.Component { render () { const {todos} = this.props return (
        { todos.map((todo, index) =>
      • {todo}
      • ) }
      ) } } TodoList.propTypes = { todos: PropTypes.array.isRequired } // 渲染应用组件标签 ReactDOM.render(, document.getElementById('example'))

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

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

      组件组合使用_总结&

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

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

      组件_收集表单数据&

      交互现做绑定事件,

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

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

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

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

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

      
        /*
        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 (
              
                
                  用户名:
                  
                 
                
                  密码:
                   this.pwdInput = input} />
                 
                
              
            )
          }
        }
        
        ReactDOM.render(, document.getElementById('example'))
      

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

      (组件_生命周期&)

      组件_生命周期&

      传递文本msg,通过props显示

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

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

      Render第一次渲染显示,

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

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

      小数加减法不是刚好为0

      移除组件

      组件移除时回调

      
      
        /*
        需求: 自定义组件
          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{opacity:this.state.opacity}}{this.props.content} {
            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 (
            
      请发表对React的评论
      ) } } 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 (
            
      用户名
      评论内容
      提交
      ) } } 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 (
            
      评论回复: { display: display }}暂无评论,点击左侧添加评论!!!
        { comments.map((comment, index) => { console.log(comment) return }) }
      ) } } 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 (
            
    • 删除

      {comment.username}说:

      {comment.content}

    • ) } } 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对象

      
      
      
      
      
        /*
        需求:
          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 loading...
            } else {
              return (
                
                  most star repo is {repoName}
                
              )
            }
          }
        }
      
        ReactDOM.render(, document.getElementById('example'))
      

      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 (
            
      Search Github Users
      ) } }
      /**
       * 上部的搜索模块
       */
      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 (
            
      this.nameInput = input)}/> Search
      ) } } 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 Enter name to search
          } else if (this.state.loading) {
            return Loading result...
          } else if (this.state.error) {
            return {this.state.error}
          } else {
            return (
              
      { this.state.users.map((user) => (
      {width: '100px'}} alt='user'/

      {user.login}

      )) }
      ) } } } 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请求。

        

      test1 push test2 回退 前进 replace test3 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) })

      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 (
            
      React Router Demo
      {/*导航路由链接*/} About Home
      {/*可切换的路由组件*/}
      ) } }
      import React from 'react'
      export default function About() {
        return 
      About组件内容
      }
      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 (
          
      Home组件内容
      • News
      • Message
      ) }
      import React from 'react'
      
      export default class News extends React.Component {
        state = {
          newsArr: ['news001', 'news002', 'news003']
        }
      
        render () {
          return (
            
        { this.state.newsArr.map((news, index) =>
      • {news}
      • ) }
      ) } }
      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 (
            
        { this.state.messages.map((m, index) => { return (
      • {m.title}     this.ShowDetail(m.id)}>查看详情(push)  this.ShowDetail2(m.id)}>查看详情(replace)
      • ) }) }

      返回  前进 

      ) } }
      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 (
          
      • ID: {md.id}
      • TITLE: {md.title}
      • CONTENT: {md.content}
      ) }

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

      (NavLink组件包装优化&)

      NavLink组件包装优化&

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

      import React from 'react'
      import {NavLink} from 'react-router-dom'
      
      export default function MyNavLink(props) {
        return 
      }

      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 (
            
      提交
      ) } }

      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(
        (
          
            
          
        ),
        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 (
            

      click {count} times {' '}

      1 2 3 {' '} +{' '} -{' '} increment if odd{' '} increment async
      ) } }

      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(
          ,
          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 (
            

      click {this.props.store.getState()} times {' '}

      1 2 3 {' '} + {' '} - {' '} increment if odd {' '} increment async
      ) } }

      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 (
            

      click {this.props.count} times {' '}

      1 2 3 {' '} +{' '} -{' '} increment if odd{' '} increment async
      ) } }

      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(
        (
          
            
          
        ),
        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(
        (
          
            
          
        ),
        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 (
            
      请发表对React的评论
      ) } } 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 (
            
      评论回复: { display: display }}暂无评论,点击左侧添加评论!!!
        { comments.map((comment, index) => { console.log(comment) return }) }
      ) } } 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 (
            
      用户名
      评论内容
      提交
      ) } } 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 (
            
    • 删除

      {comment.username}说:

      {comment.content}

    • ) } } CommentItem.propTypes = { comment: PropTypes.object.isRequired, index: PropTypes.number.isRequired, deleteComment: PropTypes.func.isRequired } export default connect( null, {deleteComment} )(CommentItem)

       

       

       

       

       

       

    关注
    打赏
    1665965058
    查看更多评论
    立即登录/注册

    微信扫码登录

    0.1370s