react(上)

73 阅读29分钟

全家桶

1.React介绍

React起源与发展

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决

定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源

了。

为什么要学习React

传统的JavaScript或者之前学习的jquery 操纵DOM 非常的繁琐(编码麻烦),并且效率低下(因为只要我们操纵DOM 那么浏览器就会频繁的对页面进行绘制与排列 )

                document.getElementById("xxxx");
        document.getElementsByClassName("xxx");
        document.getElementsByTagName("xxxx");
                $(".xxx")
                $("#xxx")
        ...
        ...

传统的JavaScript操纵DOM 浏览器会进行大量的重绘重排

传统的JavaScript没有组件化编码方案,代码复用性非常低

React是什么?

官方: React是一个用于构建用户界面的javascript库

react是一个将数据渲染为HTML的开源javascript库

React的特性

1.采用组件化模式 声明式编码 提高开发效率以及组件化复用性

2.在ReactNative中可以使用React语法进行移动端开发

3.使用虚拟DOM+diff算法尽量减少与真实DOM的交互

虚拟Dom--VirtualDom与diff算法

传统的dom操纵会在数据改变的时候 重新把页面的所有内容都绘制一遍 (哪怕是10000条数据 之多了一条 那么他也会重新渲染10001次)

虚拟dom--快减少更新次数 减少更新区域 提高性能

虚拟dom相当于在js和真实dom中间加了一个缓存。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都首先重新构建整个DOM树(减少页面更新次数),然后React将当前整个DOM树和上一次的DOM树进行对比(DOM Diff算法-计算出虚拟DOM中真正变化的部分),得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。

tupian7

React与传统MVC的关系

轻量级的视图层库!

React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开

发模式;React 构建页面 UI 的库。可以简单地理解为,React 将界面分成了各个独立的小块,每一个块

就是组件,这些组件之间可以组合、嵌套,就成了我们的页面。

2.HelloWord16.8版本

16.8是react的一个分水岭 其中有很多新增特性 在企业中使用中使用

跟多高版本的特性在后面会慢慢学到

1.获取依赖包

(1)react.js文件是创建React元素和组件的核心文件

(2)react-dom.js文件用来把React组件渲染为DOM,此文件依赖于react.js文件,需在其后被引入。

(3)Babel的主要用途是将ES6转成ES5 同时可以把JSX 语法转换新标准的JavaScript代码让现今浏览器兼容的代码

tupiana

2.编写代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 1.引用react核心库文件 -->
    <script src="react.16.8.6.js"></script>
    <!-- 2.引用react支持dom操作的react-dom -->
    <script src="react-dom.16.8.6.js"></script>
    <!-- 3.引用babel -->
    <script src="babel.min.js"></script>
</head>
<body>
    <!-- 4.创建一个容器 用来容纳后续渲染内容 -->
    <div id="demoDiv"></div>

    <script type="text/babel">/* 5.此处不要错了 让babel解析其中的内容*/
        // 6.创建虚拟dom
        let VDom= <h1>你好么么哒!!!</h1>
        // 7.把创建的虚拟dom渲染到页面上
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

    </script>
</body>
</html>

思考

虽然react 官方推荐我们使用jsx的方式来创建虚拟dom 但是有同学就会好奇 为什么官方要推荐使用jsx来创建虚拟dom

3.VirtualDom的两种创建方式

我们要完成如下图的页面dom

tupiana

方式1 使用jsx来创建虚拟DOM

其实jsx的方式创建虚拟dom我们刚才在写helloword的时候已经使用过了 我们只需要稍加改造就行了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>

    <script type="text/babel">
        //使用()代表是个整体 直接添加id 与 内容
        let VDom=(
                <h1 id="xixi">
                    <span>你坏</span>
                </h1>
                ) 
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

    </script>
</body>
</html>

方式2 使用原生js来创建虚拟DOM

原生js中使用 document.createElement()来新建dom节点

在我们引用React之后 可以使用React.createElement(标签名,标签属性,标签内容)来创建虚拟dom

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <!-- 由于使用原生 那么就不需要babel了 -->
    <!-- <script src="babel.min.js"></script> -->
</head>
<body>
    <div id="demoDiv"></div>
    <!-- 由于使用原生那么就不用babel -->
    <script type="text/javascript">
          
        // let VDom=React.createElement(标签名,{标签属性},标签内容)
        let VDom=React.createElement("h1",{id:"xixi"},React.createElement("span",{},"你好"))
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

    </script>
</body>
</html>

总结

jsx出现的原因 就是因为 传统方式创建虚拟DOM太麻烦了 所以才需要JSX来创建虚拟DOM简化创建虚拟DOM的复杂度(也可以理解为jsx就是传统dom操作的语法糖--语法糖也叫糖衣语法就是用简单的语法完成之前复杂的事情

4.虚拟DOM与真实DOM区别

我们查看下虚拟dom与真实dom到底是什么?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>

    <script type="text/babel">
        
        let VDom=(
                <h1 id="xixi">
                    <span>你坏</span>
                </h1>
                ) 
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

        console.log("虚拟dom",VDom);
        console.log("真实dom",document.getElementById("demoDiv"));

    </script>
</body>
</html>

大家会发现浏览器中显示 虚拟dom就是一个我们常见的普通对象

tupian4

但是虚拟dom和真实dom里面是什么呢?

我们使用debugger看一下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>

    <script type="text/babel">
        
        let VDom=(
                <h1 id="xixi">
                    <span>你坏</span>
                </h1>
                ) 
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

        let ZDom=document.getElementById("demoDiv")
        console.log("虚拟dom",VDom);
        console.log("真实dom",ZDom);
        // 设置断点
    
        debugger

    </script>
</body>
</html>

在浏览器中查看会发现

虚拟dom

tupian5

真实dom

tupian6

对比后会发现

虚拟dom会比较 而真实dom 比较 因为虚拟dom只需要在react内部在使用需要使用那么多属性

总结

1.虚拟dom就是一个我们常见的普通对象

2.虚拟dom会比较 而真实dom 比较 因为虚拟dom只需要在react内部在使用需要使用那么多属性

3.虚拟dom最终会被react转换成真实dom 展现在页面上

5.jsx语法规则

jax=javascript xml (xml是一种早起存储数据的格式 是一种要求语法非常严谨的数据格式--现在都使用json)

jsx对语法的要求非常的严格 严格到变态

一个根标签

多行标签需要有一个容器进行包裹

语法严谨

标签必须闭合 必须按照w3c规范来编写

Jsx变量 属性插值 与 注释

定义虚拟dom的时候不加双引号 同时jsx遇见{} 会当js表达式解析 遇见<>当html解析

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>

    <script type="text/babel">
        let text="xixi";
        let demotext="你好"

        let VDom=(
                <h1 id={text}>
                    {/*插入变量*/}
                    <span>{demotext}</span>
                </h1>
                ) 
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

    </script>
</body>
</html>

总结:

插入变量: 因为在jsx中遇见{}就会把里面的东西当成js表达式 解析 所以如果我们想插入变量 就把这个变量放到{我是变量}

插入属性: 因为在jsx中遇见{}就会把里面的东西当成js表达式 解析 所以我们如果想给属性插入变量 就把这个变量直接放到 属性={变量}

插入注释:因为在jsx中遇见{}就会把里面的东西当成js表达式 解析 所以我们注释 {/* 我是注释 */}

样式

行内样式

行内样式需要写入一个样式对象

注意语法 第一个{}是jsx的语法 第二个{}是对象的语法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>

    <script type="text/babel">
        let text="xixi";
        let demotext="你好"

        let VDom=(
                <h1 id={text}>
                    <span style={{color:"red",backgroundColor:"yellow"}}>{demotext}</span>
                </h1>
                ) 
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

    </script>
</body>
</html>

类样式

jsx中类名不能使用class来设置(因为class是js的关键字)使用className来设置

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
    <style>
        .demo{
            color:red;
            background-color: yellow;
        }
    </style>
</head>
<body>
    <div id="demoDiv"></div>

    <script type="text/babel">
        let text="xixi";
        let demotext="你好"

        let VDom=(
                <h1 id={text}>
                    <span className="demo">{demotext}</span>
                </h1>
                ) 
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

    </script>
</body>
</html>

数据遍历

在日常工作中我们进场需要把数据进行遍历展示到页面中 那么在jsx中怎么遍历呢?

特殊情况(工作不会遇见)

如果数据是一个数组 那么react会自动把这个数据中的内容遍历出来并且展示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
          
        let arr=["EZ","VN","MF","NOC"]
        let arrb=[<li>EZ</li>,<li>VN</li>,<li>MF</li>,<li>NOC</li>]
        let VDom=(
            <div>
                <ul>
                {/*
                    大家会发现ract会把我们的数据进行遍历
                    但是这样也有问题就是 ul中不会自动生产标签
                */}
                    {arr} 
                </ul>   
                
                <ul>
                {/*
                    虽然这样可以遍历出来数据 但是工作中 没有那个后端会给我们返回arrb这										样的数据  所以这种方式行不通
                */}
                    {arrb}
                </ul>   
            </div>
        )
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

    </script>
</body>
</html>

那么怎么办呢?

遍历数据

首先我们在遍历数据的时候可以使用 数组的map方法 因为map方法可以遍历数据并且返回出新的内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
          
        let arr=["EZ","VN","MF","NOC"]
        
        let VDom=(
            <div>
               <ul>
                    {
                        arr.map((v,i)=>{
                            return (
                                <li key={i}>{v}</li>
                            )
                        })
                    }
                </ul>
            </div>
        )
        ReactDOM.render(VDom,document.getElementById("demoDiv"))

    </script>
</body>
</html>

注意

在jsx中 一对大括号中 放的是js表达式

表达式:通过计算可以返回结果的公式 所以上面的map就是一个表达式 因为map可以返回一个新的结果

但是注意 jsx大括号中不能放置 if switch for 这些js语句 因为他们只是控制程序的执行顺序 不能返回新的结果 所以他们不是表达式

但是注意 jsx大括号中不能放置 if switch for 这些js语句 因为他们只是控制程序的执行顺序 不能返回新的结果 所以他们不是表达式

但是注意 jsx大括号中不能放置 if switch for 这些js语句 因为他们只是控制程序的执行顺序 不能返回新的结果 所以他们不是表达式

6 面向组件编程

组件与组件化 模块与模块化

模块与模块化

模块:用来封装可以重复使用的js代码块

模块化:整个项目都是使用模块的方式来完成的

组件与组件化

组件: 用来封装重复使用的ui代码块

组件化:整个项目都是使用组件的方式来完成的

组件的概念

组件就是把ui部分拆分成一个个独立的并且可以重复使用的部件 在吧这些部件拼装在一起 形成一个页面

组件的设计目的是提高代码复用率,降低测试难度和代码的复杂程度。

1 .提高代码复用率:组件将数据和逻辑进行封装。 2 .降低测试难度:组件高内聚低耦合(各个元素高集成度低关联性),很容易对单个组件进行测试。 3 .代码的复杂程度:直观的语法,可以极大提高可读性。

tutu1

React Dev Tools

注意:React Dev Tools是谷歌浏览器的插件 所以别的浏览器不行

在开发原生js的时候,我们经常使用浏览器自带的开发者工具,它足以帮助我们查看和调试js中变量的各种信息。

对于react框架来说,因为它是采用动态渲染生成的代码结构,因此,我们需要一种可以分析react代码结构和变量状态的工具,而react dev tools 就是这样的工具

安装

安装方式1

如果你有科学上网工具 直接可以在谷歌浏览器商店中搜索下载

安装方式2

打开谷歌浏览器--》右上角三个点--》更多工具--》扩展程序--》最最最最关键的一步就是在右上角有个开发者模式的选项打开--》选择左侧上方加载已经解压的扩展程序--》找到对应提供的文件路径即可

tutu2

设置快捷使用

如果想设置快捷使用

第一步

tutu2

第二步

tutu2

默认是灰色的 只要你访问的页面是由react写的 那么他就会显示正常

tutu2

组件分类

函数组件/无状态组件/工厂组件

函数式组件的基本意义就是,组件实际上是一个函数 这个函数返回一段jsx

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
      	// 首字母大写
        // 首字母大写
        // 首字母大写
        // 首字母大写
        function Fun(){
            return (
                <div>
                    <h1>我是一个函数组件</h1>    
                </div>
            )
        }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

调试

使用刚才安装的调试工具查看

too1

tu1

类组件

类组件,顾名思义,也就是通过使用ES6类的编写形式去编写组件,该类必须集成React.Component 其中有一个render的渲染方法 里面有段jsx

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
        render(){
            return (
                <div>
                    <h1>我是一个类组件</h1>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

区别

函数组件的创建形式使代码的可读性更好,并且减少了大量冗余的代码,大大的增强了编写一个组件的便利

函数组件不会被实例化,整体渲染性能得到提升,无实例化过程也就不需要分配多余的内存,从而性能得到一定的提升。

函数组件由于没有实例化过程,所以无法访问组件this中的对象,若想访问this就不能使用这种形式来创建组件

函数组件无法访问生命周期的方法

思考

既然上面说了 函数组件没有this 而类组件中有this 那么类组件中的这个this里面有什么?

tu2

7 属性1-state 组件状态(数据/变量)

state状态机

大家发现上图中有state这个属性

state属性:

  1. state 是组件对象最重要的属性,值是对象(可以包含多个 key-value 的组合)
  2. 组件被称为“状态机”(状态机制),通过更新组件的 state 来更新对应的页面显示(重新渲染组件)

1.初始化state 读取

默认情况下 state中是null 我们需要在类组件中初始化 所以我们可以放到 comstructor中进行初始化操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
        // 初始化
        constructor(props){
            super(props)
            // 1.设置state
            this.state={
                bool:true
            }
        }
        render(){
            return (
                <div>
                {/*2.使用state状态*/}
                    <h1>我是一个类组件--{this.state.bool?"你好":"你坏"}</h1>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

2.修改state

更新状态需要调用 this.setState() 方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
        constructor(props){
            super(props)
            this.state={
                bool:true
            }
        }
        fun=()=>{
            // 修改需要使用setState
            this.setState({
                bool:!this.state.bool
            })
        }
        render(){
            return (
                <div>
                    <h1 onClick={this.fun}>我是一个类组件--{this.state.bool?"你好":"你坏"}</h1>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

setState调用后发生了什么

setState是异步的

(如果有大量数据修改的话不会因为修改数据而造成程序的卡顿)

import React, { Component } from 'react'

export default class demob extends Component {
    // 初始化state状态数据需要放到constructor中进行初始化
    // es6中继承的规则中得知 子类是可以不写constructor 他会在实例化的时候
    // 自动补充一个

    // 但是如果你写了 那么必须在其中写super()  因为super就是调用父类的构造方法
    // 此时子类才有this
    constructor() {
        super()   
        // 初始化state 
        this.state={
            text:"我是字符串",
            num:888,
            bool:true,
            arr:[1111,2222,333],
            obj:{
                name:"xixi"
            }
        }
    }

    fun=()=>{
        // 修改state的数据
        // this.setState({
        //     num:123,
        //     text:"xixi"
        // })
        // 下面的console.log打印的结果是修改之后的  还是修改之前的?
        // 是修改之前的结果  所以从而证明了setState是一个异步任务
        // console.log(this.state.num)

        // 但是我就是想setState修改完数据之后  打印新的结果怎么办?
        // 因为setState是异步   异步都会有回调函数
        this.setState({
            num:123,
            text:"xixi"
        },()=>{
            // setState第二个参数是一个回调函数  当数据修改完他才会调用
            console.log(this.state.num)

        })
     
    }


    render() {
        return (
            <>
                {/* 2.使用state数据 */}
                <h1>我是测试state使用的例子----{this.state.num}---{this.state.text}</h1>
                <button onClick={this.fun}>点我修改</button>

            </>
        )
    }
}

调用了setState之后会自动触发render渲染

render就是渲染方法 只有render方法执行了 那么页面才会根据数据的改变而随之发生改变

render就是渲染方法 只有render方法执行了 那么页面才会根据数据的改变而随之发生改变

render就是渲染方法 只有render方法执行了 那么页面才会根据数据的改变而随之发生改变

render就是渲染方法 只有render方法执行了 那么页面才会根据数据的改变而随之发生改变

state的简写写法

因为类中可以直接编写赋值语句所以我们的state也可以直接创建 简化了写法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
        // 创建state
            state={
                bool:true
            }
        fun=()=>{
            // 修改需要使用setState
            this.setState({
                bool:!this.state.bool
            })
        }
        render(){
            return (
                <div>
                    <h1 onClick={this.fun}>我是一个类组件--{this.state.bool?"你好":"你坏"}</h1>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

hook-useState

useState

useState 是reactHOOK给我们提供的 最基本最常用的一个HOOK 主要作用就是用来管理当前本地的状态

useState() 返回值是一个数组(长度为2)数组的第一项标识的是当前的值 数组的第二项标识的时候修改这个值的函数

let [xiaoming , setXiaoming]=useState(初始值)

创建与读取

import {useState} from "react"
let Funcom=()=>{
    // 使用useState()c创建函数组件的状态
    let [xiaoming,setxiaoming]=useState("你好么么哒!!!")
    return (
        <div>
            {/* 读取 */}
            <h1>我是一个函数组件--{xiaoming}</h1>
        </div>
    )
}
export default Funcom

修改

import {useState} from "react"
let Funcom=()=>{
    // 使用useState()创建函数组件的状态
    let [xiaoming,setxiaoming]=useState("你好么么哒!!!")
    return (
        <div>
            {/* 读取 */}
            <h1>我是一个函数组件--{xiaoming}</h1>
            {/* 修改数据 */}
            <button onClick={()=>{setxiaoming(xiaoming="你好呵呵哒")}}>点我修改</button>
        </div>
    )
}
export default Funcom

创建多个状态呢

1.你写多个useState

import {useState} from "react"
let Funcom=()=>{
    // 1.你写多个useState了解
    let [xiaoming,setxiaoming]=useState("1")
    let [xiaohong,setxiaohong]=useState("2")
  
    return (
        <div>
          {xiaoming}---{xiaohong}
        </div>
    )
}
export default Funcom

2.一次行创建多个值

import {useState} from "react"
let Funcom=()=>{
    // 1.你写多个useState了解
    // let [xiaoming,setxiaoming]=useState("1")
    // let [xiaohong,setxiaohong]=useState("2")
  
    // 2.一次性创建多个值
        let [xiaoming,setxiaoming]=useState({
            dataa:"第一个值1",
            datab:"第一个值2",
            datac:"第一个值3",
            datad:"第一个值4",
            datae:"第一个值5",
            dataf:"第一个值6"
        })
    return (
        <div>
          {/* {xiaoming}---{xiaohong} */}

          {xiaoming.dataa}----{xiaoming.datad}
        </div>
    )
}
export default Funcom

一次性创建多个值怎么修改

1.多个useState 要修改的话就依次调用其中的修改方法

2.一次行创建多个值的方式如何修改呢

import {useState} from "react"
let Funcom=()=>{
    // 1.你写多个useState了解
    // let [xiaoming,setxiaoming]=useState("1")
    // let [xiaohong,setxiaohong]=useState("2")
  
    // 2.一次性创建多个值
        let [xiaoming,setxiaoming]=useState({
            dataa:"第一个值1",
            datab:"第一个值2",
            datac:"第一个值3",
            datad:"第一个值4",
            datae:"第一个值5",
            dataf:"第一个值6"
        })

       let fun=()=>{
        //    一次性创建多个的修改操作   不要忘了保留原始数据
        setxiaoming({...xiaoming,datad:"我被改了"})
       }

    return (
        <div>
          {/* {xiaoming}---{xiaohong} */}

          {xiaoming.dataa}----{xiaoming.datad}
          <button onClick={fun}>点我修改</button>
        </div>
    )
}
export default Funcom

总结state

state就是react中用来创建状态的 状态就是数据

在我们使用state的时候有两种情况 分别是函数组件和类组件

类组件 中使用state来进行状态的创建 在修改的时候必须调用setState来进行修改 因为setState是异步的 并且会自动触发render重新渲染 从而让状态数据改变之后 页面也跟着改变

函数组件:他有另外一个名字叫无状态组件 也就是说默认情况不能使用state状态 如果要使用 我们可以使用react16.8新增的一个特性叫HOOK中的useState来进行状态的创建 useState接收一个参数 并且返回数组长度为2的一个内容 第一个是保存的变量 第二个是修改的动作

8 事件

在react中事件的绑定 使用小驼峰命名法

例:onclick 在react中 onClick onchange 在react中 onChange

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

基本事件操纵

事件绑定 使用小驼峰命名法 鼠标左键点击事件 onclick------》onClick onchange------》onChange

修改this指向

想想函数中的this指向

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
        fun(){
            console.log(this)//undefined
        }
        render(){
            return (
                <div>
                  <button onClick={this.fun}>点我</button>  
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

this是 undefined 所以我们在使用setState等属性方法的时候就会报错

怎么解决呢?

方式1 bind()

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
        fun(){
            console.log(this)
        }
        render(){
            return (
                <div>
                    {/*bind方式修改this*/}
                  <button onClick={this.fun.bind(this)}>点我</button>  
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

方式2 通过创建箭头函数

在创建函数的时候创建一个箭头函数

方式3 在constructor中提前对事件进行绑定

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
        constructor(props){
            super(props)
            // 提前绑定this
            this.fun=this.fun.bind(this)
        }

        fun(){
            console.log(this)
        }
        render(){
            return (
                <div>
                  <button onClick={this.fun}>点我</button>  
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

方式4 将事件调用的写法改为箭头函数的形式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
       
        fun(){
            console.log(this)
        }
        render(){
            return (
                <div>
                    {/*使用箭头函数调用*/}
                  <button onClick={()=>{this.fun()}}>点我</button>  
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

函数参数传递

因为在react中函数调用的时候不加() 那我我们如果要传递函数的实参怎么传递?

方式1

使用bind方式进行传递

 <button onClick={this.fun.bind(this,"我是实参1","我是实参2")}>点我传递函数实参</button>

方式2

使用箭头函数调用函数进行传递

<button onClick={()=>{this.funb(1111,2222)}}>点我传递实参2</button>

事件对象event

使用event

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Fun extends React.Component{
       
         // 直接传值event即可得到事件对象
        fun=(event)=>{
             console.log("您在输入框中是",event.target.value)
        }
        render(){
            return (
                <div>
                    <h1>事件对象</h1>
                    <input type="text" onInput={this.fun}/> 
                </div>
            )
        }
       }
        ReactDOM.render(<Fun/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

阻止事件传播与默认行为

同原生js

阻止特定事件的默认行为:event.preventDefault()

立即停止事件在 DOM 层次中的传播,即阻止事件冒泡:event.stopPropagation()

总结

事件绑定的方式

如何修改this指向 记得越多越好

函数参数传递

事件对象与修饰符

9 props

props功能在于组件间通信(父子组件)

注意

Props对于使用它的组件来说,是只读的。一旦赋值不能修改。也就是说props的值是不可变的只能在渲染的时候传入无法动态赋值。

父子组件

组件之前的嵌套 就能形成最基本的父子组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Zi extends React.Component{
        render(){
            return (
                <div>
                    <h1></h1>    
                </div>
            )
        }
       }
       class Fu extends React.Component{
        render(){
            return (
                <div>
                    <h1></h1>
                    <Zi/>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fu/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

props基本使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Zi extends React.Component{
        render(){
            return (
                <div>
                    {/*创建props属性*/}
                    <h1>子--{this.props.name}</h1>    
                    <h1>子--{this.props.age}</h1>    
                    <h1>子--{this.props.sex}</h1>    
                </div>
            )
        }
       }
       class Fu extends React.Component{
        render(){
            return (
                <div>
                    <h1></h1>
                    {/*
                        传递参数
                        props的本质是 标签的属性
                        组件就是自定义标签
                        所以可以在标签中进行传递参数
                    */}
                    <Zi name="xixi" age="18" sex="男"/>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fu/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

props 进阶--批量传递

上面props的使用中

子组件: 出现了大量的 重复代码 this.props

父组件: 在传递props参数的时候 数据少不影响 但是数据多了之后 会导致很难看 并且 后期调用后台参数的时候 需要一个个的赋值 很麻烦

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Zi extends React.Component{
        render(){
            // 使用解构快速取出对象的值
            let {name,age,sex}=this.props
            return (
                <div>
                    
                    <h1>子--{name}</h1>    
                    <h1>子--{age}</h1>    
                    <h1>子--{sex}</h1>    
                </div>
            )
        }
       }
       class Fu extends React.Component{
        render(){
            let obj={
                name:"xixi",
                age:18,
                sex:"男"
            }
            return (
                <div>
                    <h1></h1>
                    {/*
                        使用扩展运算符
                    */}
                    <Zi {...obj}/>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fu/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

props验证

我们之前可以给组件中传递任何数据类型的数据 但是有的时候我们需要限制只能传递指定的类型 那么这个时候我们就可以使用props验证来完成

注意

react 15.5之前Props 验证使用 propTypes,它可以保证我们在应用组件的时候可以正确的传递值

tututut1

props验证只会在控制台给开发者一个错误的警告 不会影响页面的展示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

    <!-- 引用prop-types库 -->
    <script src="./prop-types.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Zi extends React.Component{
        render(){
            // 使用解构快速取出对象的值
            let {name,age,sex}=this.props
            return (
                <div>
                    
                    <h1>子--{name}</h1>    
                    <h1>子--{age}</h1>    
                    <h1>子--{sex}</h1>    
                </div>
            )
        }
       }

        //    props验证
        Zi.propTypes={
            name:PropTypes.number,//设置类型为string
            age:PropTypes.string.isRequired//设置为字符串类型  同时不能为空
        }

       class Fu extends React.Component{
        render(){
            let obj={
                name:"xixi",
                // age:18,
                sex:"男"
            }
            return (
                <div>
                    <h1></h1>
                    <Zi {...obj}/>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fu/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

props默认值--defaultProps

设置props默认值之后 在没有传递数据的时候会自动使用设置好的默认值进行页面的显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

    <!-- 引用prop-types库 -->
    <script src="./prop-types.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       class Zi extends React.Component{
        render(){
            // 使用解构快速取出对象的值
            let {name,age,sex}=this.props
            return (
                <div>
                    
                    <h1>子--{name}</h1>    
                    <h1>子--{age}</h1>    
                    <h1>子--{sex}</h1>    
                </div>
            )
        }
       }

        //    props验证
        Zi.propTypes={
            name:PropTypes.number,//设置类型为string
            age:PropTypes.string.isRequired//设置为字符串类型  同时不能为空
        }

        // 设置默认值
        Zi.defaultProps={
            sex:"我是默认值"
        }

       class Fu extends React.Component{
        render(){
            let obj={
                name:"xixi",
                // age:18,
                // sex:"男"
            }
            return (
                <div>
                    <h1></h1>
                    <Zi {...obj}/>    
                </div>
            )
        }
       }
        ReactDOM.render(<Fu/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

函数组件使用props

在函数组件中使用props和类组件 有所不同 类组件中使用this.props.xxx 但是在函数组件中需要把props当成一个函数的形参传递进入 才能正常使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

    <!-- 引用prop-types库 -->
    <script src="./prop-types.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
       let Zi=(props)=>{
        // 使用解构快速取出对象的值
        let {name,age,sex}=props
            return (
                <div>
                    
                    <h1>子--{name}</h1>    
                    <h1>子--{age}</h1>    
                    <h1>子--{sex}</h1>    
                </div>
            )
       }

        //    props验证
        Zi.propTypes={
            name:PropTypes.string,//设置类型为string
            age:PropTypes.string.isRequired//设置为字符串类型  同时不能为空
        }

        // 设置默认值
        Zi.defaultProps={
            sex:"我是默认值"
        }

       let Fu=()=>{
        let obj={
                name:"xixi",
                age:18,
                // sex:"男"
            }
            return (
                <div>
                    <h1></h1>
                    <Zi {...obj}/>    
                </div>
            )
       }
        ReactDOM.render(<Fu/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

总结:

props是组件用来接收外部传递进来的数据的一个技术 在ract中使用props有两种方式

函数组件 把props当成函数的形参传入即可直接使用

类组件 使用this.props.xxx的方式进行使用

在props中还有props验证 props验证在15。5版本之后 必须要单独引用一个库叫prop-type 引用后直接定义

props偶默认值的写法 defaultprops来定义默认值

10 ref

类组件

React提供的这个ref属性(不能在无状态组件上使用 ref 属性,因为它们没有实例 ref属性是需要有组件实例才能使用)表示为对组件真正实例的引用其实就是ReactDOM.render()返回的组件实例

ref 返回是真实的dom节点。

一句话总结: 标识组件内部的元素

方式1 字符串方式

使用ref属性 标识组件 在使用this.refs.xxx 获取真实dom节点

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

    <!-- 引用prop-types库 -->
    <script src="./prop-types.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component{
            fun=()=>{
                // 获取ref绑定的dom元素
                console.log(this.refs.demoInput.value)
            }
            render(){
                return (
                    <div>
                       {/*设置ref绑定dom元素*/} 
                        <input type="text" ref="demoInput"/>
                        <button onClick={this.fun}>点我得到输入框的值</button>    
                    </div>
                )
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

当前方式在React官方已经不推荐使用 后续内容已经废弃了这种写法

当前方式在React官方已经不推荐使用 后续内容已经废弃了这种写法

因为字符串的方式 效率会有问题

tu10

方式2 回调函数方式

回调函数就是在dom节点或组件上挂载回调函数,函数的入参是dom节点,达到的效果与字符串形式是一样的,都是获取其引用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

    <!-- 引用prop-types库 -->
    <script src="./prop-types.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component{
            fun=()=>{
                // 获取ref绑定的dom元素
                console.log(this.inputDom.value)
            }
            render(){
                return (
                    <div>
                       {/*设置ref绑定dom元素*/} 
                        <input type="text" ref={(demo)=>{this.inputDom=demo}}/>
                        <button onClick={this.fun}>点我得到输入框的值</button>    
                    </div>
                )
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

扩展问题

tu11

官网中给我们介绍说内联函数的方式 会在更新的时候执行两次

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

    <!-- 引用prop-types库 -->
    <script src="./prop-types.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component{
            state={
                name:"xixi"
            }
            fun=()=>{
              
                console.log(this.inputDom.value)
            }
            funb=()=>{
                this.setState({
                    name:"haha"
                })
            }
            render(){
                return (
                    <div>
                       {/*1.我们在绑定ref的回调中 添加console查看是否执行*/} 
                        <input type="text" ref={(demo)=>{this.inputDom=demo;console.log("回调函数方式")}}/>
                        <button onClick={this.fun}>点我得到输入框的值</button> 
                        
                        {/*2.添加按钮修改一个输入  让render重新调用 模拟更新效果*/}
                        <h1>{this.state.name}</h1>
                        <button onClick={this.funb}>点我修改数据</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

大家会发现这种方式会执行两次 因为我们之前写的方式是内联函数的方式 所以不想执行两次 我们就不是用内联函数的方式 使用绑定函数的方式即可解决

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

    <!-- 引用prop-types库 -->
    <script src="./prop-types.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component{
            state={
                name:"xixi"
            }
            fun=()=>{
              
                console.log(this.inputDom.value)
            }
            funb=()=>{
                this.setState({
                    name:"haha"
                })
            }

            demoref=(demo)=>{
                this.inputDom=demo;
                console.log("回调函数方式");
            }
            render(){
                return (
                    <div>
                       {/*1.我们在绑定ref的回调中 添加console查看是否执行*/} 
                        <input type="text" ref={this.demoref}/>
                        <button onClick={this.fun}>点我得到输入框的值</button> 
                        
                        {/*2.添加按钮修改一个输入  让render重新调用 模拟更新效果*/}
                        <h1>{this.state.name}</h1>
                        <button onClick={this.funb}>点我修改数据</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

注意

回调函数方式在工作中 使用哪一种影响都不大 只是让大家了解下其中的问题 在被问到的时候可以了解

方式3 createRef方式

React.createRef() 当被调用的时候会返回一个可以存储ref标识的dom 的一个容器

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

    <!-- 引用prop-types库 -->
    <script src="./prop-types.js"></script>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component{
            // 1.创建出用来存储refdom的容器
            // React.createRef() 当被调用的时候会返回一个可以存储ref标识的dom 的一个容器
            myRef=React.createRef()
         
            fun=()=>{
                // 3.使用
                console.log(this.myRef.current.value)
            }
        
            render(){
                return (
                    <div>
                        {/*2.绑定*/}
                        <input type="text" ref={this.myRef}/>
                        <button onClick={this.fun}>点我得到输入框的值</button> 
                     
                    </div>
                )
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

函数组件使用ref

函数组件默认不能使用ref 所以我们需要使用react16.8新增的HOOK中的useRef帮助我们使用ref

useRef就是可以让函数组件使用ref的一个技术

import {useRef} from "react"
let Funcom=()=>{
//    1.创建出useRef
    let xiaoming=useRef(null)

    let fun=()=>{
        // 3.获取
        console.log(xiaoming.current.value);
    }
    return (
        <div>
            {/* 2.绑定使用 */}
          <input type="text" ref={xiaoming}/>
          <button onClick={fun}>点我得到输入框的值</button>
        </div>
    )
}
export default Funcom

11 收集表单数据

受控组件与非受控组件

两者都是呈现 并且收集HTML 表单元素数据的 React 组件。

非受控组件

非受控组件就是在react中使用Ref技术获取DOM中表单的数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            inputDom=React.createRef()

            handleSubmit=(event)=>{
                event.preventDefault();
                alert('提交的名字: ' + this.inputDom.current.value);
         
            }

            render() {
                return (
                    <form onSubmit={this.handleSubmit}>
                        <label>
                            名字:
                            <input type="text"  ref={this.inputDom}/>
                        </label>
                        <input type="submit" value="提交" />
                    </form>
                );
            }
        }

        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,可以快速编写代码,同时可以减少你的代码量。

但是官方是这样说的 所以还是不太建议大家日常使用非受控组件

tu13

受控组件

在react中 表单中的输入数据 被state所存储并且管理的组件 就是受控组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            state = { 
                    value: '' 
                };

            handleChange=(event)=> {
                this.setState({ value: event.target.value });
            }

            handleSubmit=(event)=>{
                alert('提交的名字: ' + this.state.value);
                event.preventDefault();
            }

            render() {
                return (
                    <form onSubmit={this.handleSubmit}>
                        <label>
                            名字:
                            <input type="text" value={this.state.value} onChange={this.handleChange} />
                        </label>
                        <input type="submit" value="提交" />
                    </form>
                );
            }
        }

        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

12 组件生命周期

函数组件默认不能使用生命周期 如果要使用 那么就要使用HOOK来完成

旧版本

tu15

挂载阶段

constructor 构造器 初始化 同一个组件对象只会创建一次 注意:不能在第一次挂载到页面之前,调用setState,为了避免问题,构造函数中严禁使用setState

componentWillMount 组件准备挂载 正常情况下,和构造函数一样,它只会运行一次 可以使用setState,但是为了避免bug,不允许使用,因为在某些特殊情况下,该函数可能被调用多次

render 组件渲染虚拟dom 返回一个虚拟DOM,会被挂载到虚拟DOM树中,最终渲染到页面的真实DOM中 render可能不只运行一次,只要需要重新渲染,就会重新运行 严禁使用setState,因为可能会导致无限递归渲染

componentDidMount 组件挂载完毕 只会执行一次 可以使用setState 通常情况下,会将网络请求、启动计时器等一开始需要的操作,书写到该函数中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            constructor(props){
                super(props)

                console.log("构造器 初始化")
            }
            componentWillMount = () => {
                console.log("组件准备挂载")
            }

            componentDidMount = () => {
                console.log("组件挂载完毕")
            }
            render() {
                console.log("组件渲染虚拟dom")
                return (
                    <div>
                        <h1>挂载阶段</h1>    
                        
                    </div>
                );
            }
        }

        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

更新阶段

componentWillReceiveProps 组件接收到一个新的props时被调用 组件接收到一个新的prop时被调用 初始化不执行

shouldComponentUpdate 判断组件是否要更新 指示React是否要重新渲染该组件,通过返回true和false来指定 不写情况下,react会自动添加 并且返回true

componentWillUpdate 组件更新之前 组件即将被重新渲染

componentDidUpdate 组件更新之后 往往在该函数中使用dom操作,改变元素,这里可以操作原生的dom

更新数据 三种情况

1.setState修改数据

tu15

shouldComponentUpdate--》componentWillUpdate--》render--》componentDidUpdate

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            constructor(props){
                super(props)
                this.state={
                    text:"我是默认值xixi"
                }
            }
            update=()=>{
                this.setState({
                    text:"我变了"
                })
            }


            shouldComponentUpdate(){
                console.log("判断组件是否要更新")
                return true
            }
            
            componentWillUpdate(nextProps, nextState){
              console.log("组件更新之前")
            }

            componentDidUpdate = (prevProps, prevState) => {
                console.log("组件更新之后")
            }
            
       
            render() {
                console.log("render渲染dom")
                return (
                    <div>
                        <h1>更新阶段</h1>    
                        <h1>{this.state.text}</h1>
                        <button onClick={this.update}>点我修改</button>
                    </div>
                );
            }
        }

        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>
2.forceUpdate()修改数据

tu15

forceUpdate 强制刷新:forceUpdate就是重新render。有些变量不在state上,当时你又想达到这个变量更新的时候,页面更新 那么这个时候就可以使用forceUpdate()

调用forceUpdate()会导致组件跳过shouldComponentUpdate() 直接修改

componentWillUpdate--》render--》componentDidUpdate

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            // 不在state上的数据
            text="我是不state上的数据"

            update=()=>{
                this.text="变了"
                this.forceUpdate()
            }

            // 不会执行判断组件是否要更新
            shouldComponentUpdate(){
                console.log("不会执行判断组件是否要更新")
                return true
            }
            
            componentWillUpdate(nextProps, nextState){
              console.log("组件更新之前")
            }

            componentDidUpdate = (prevProps, prevState) => {
                console.log("组件更新之后")
            }
            
       
            render() {
                console.log("render渲染dom")
                return (
                    <div>
                        <h1>fourceUpdate更新阶段</h1>    
                        <h1>{this.text}</h1>
                        <button onClick={this.update}>点我修改</button>
                    </div>
                );
            }
        }

        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>
3.父组件更新的时候

tu15

componentWillReceiveProps --》shouldComponentUpdate--》componentWillUpdate--》render--》componentDidUpdate

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
         class Zi extends React.Component {
            componentWillReceiveProps = (nextProps) => {

                console.log("组件接收到一个新的prop时被调用。初始化不触发",nextProps)
            }
            
                render(){
                    return (
                        <div>
                            子组件--{this.props.title}
                        </div>
                    )
                }
              }
        
        class Fu extends React.Component {
            state={
                text:"我是父组件的数据"
            }
            fun=()=>{
                this.setState({
                    text:"父组件修改了"
                })
            }

            render() {
                
                return (
                    <div>
                        <h1>父组件</h1>
                        <button onClick={this.fun}>点我修改</button>
                        <Zi title={this.state.text}/>
                    </div>
                );
            }
        }

        ReactDOM.render(<Fu/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

卸载阶段--componentWillUnmount

componentWillUnmount 组件实例卸载 通常在该函数中销毁一些组件依赖的资源,比如计时器

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            fun=()=>{
                ReactDOM.unmountComponentAtNode(document.getElementById("demoDiv"))
            }
            componentWillUnmount = () => {
              console.log("卸载了")
            }
            
            
            render() {
                return (
                    <div>
                        <h1>卸载阶段</h1>    
                        <button onClick={this.fun}>点我卸载</button>
                    </div>
                );
            }
        }

        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

新版本生命周期

新生命周期图

tu16

getDerivedStateFromProps(很少用)

从字面来解释 get读取Derived衍生State状态from来自于props

它让组件在 props 发生改变时更新它自身的内部 state

去除了componentWillMount,把componentWillReceiveProps 变成 getDerivedStateFromProps

getDerivedStateFromProps 当前钩子在初始化和数据修改的时候都会触发 它应返回一个状态对象来更新 state,如果返回 null 则不更新任何内容。

我们直接测试下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            getDerivedStateFromProps() {
                 console.log("getDerivedStateFromProps")
            }
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1>    
                
                    </div>
                );
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

大家会发现出现如下警告:说当前的方法必须使用静态属性 static来实现

tu16

添加之后

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
           static getDerivedStateFromProps() {
                console.log("getDerivedStateFromProps")
            }
            
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1>    
                
                    </div>
                );
            }
        }

        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

tu16

修改之后

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            // 定义初始状态
            state={

            }
            static getDerivedStateFromProps() {
                console.log("getDerivedStateFromProps")

                // 返回状态对象 也可以返回null
                // 返回状态对象 也可以返回null
                return {
                    
                }
            } 
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1>    
                
                    </div>
                );
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

那么我们已经知道了可以返回一个对象 或者返回null

那么什么叫:它让组件在 props 发生改变时更新它自身的内部 state

首先他有两个形参 参数1传递进来的props 参数2自身的state

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            // 定义初始状态
            state={
                text:"我是state"
            }
            static getDerivedStateFromProps(nextProps, prevState) {
                console.log("参数1传递进来的props",nextProps)
                console.log("参数2自身的state",prevState)

            
                return {
                   
                }
            } 
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1> 
                        <h1>{this.state.text}</h1>   
                        <h1>{this.props.title}</h1>
                    </div>
                );
            }
        }
        ReactDOM.render(<Com title="我是title"/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

返回状态对象 如果和初始化状态重名 会被替换 不重名不影响

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
      
            state={
                text:"我是state"
            }
            static getDerivedStateFromProps(nextProps, prevState) {
                console.log("参数1传递进来的props",nextProps)
                console.log("参数2自身的state",prevState)

            
                return {
                    // 返回状态对象 如果和初始化状态重名 会被替换 不重名不影响
                    text:nextProps.title
                }
            } 
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1> 
                        <h1>{this.state.text}</h1>   
                        <h1>{this.props.title}</h1>
                    </div>
                );
            }
        }
        ReactDOM.render(<Com title="我是title"/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

但是注意getDerivedStateFromProps不是必须要使用(因为大量使用会造成代码冗余 难以维护) 因为在constructor中也可以接受props 同样可以传递给state的初始值

小例子

getDerivedStateFromProps 当前钩子在初始化和数据修改的时候都会触发

它应返回一个状态对象来更新 state (返回一个状态对象来更新state怎么用呢?)

如果返回 null 则不更新任何内容(不更新就不讨论了)。

我想把一个状态数据在初始化 和修改之后都变成大写

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="react.16.8.6.js"></script>
    <script src="react-dom.16.8.6.js"></script>
    <script src="babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            // 定义初始状态
            state={
                text:"xixi",
                age:18
            }
            static getDerivedStateFromProps(nextProps, prevState) {
                console.log("getDerivedStateFromProps")

                // 返回状态对象 如果和初始化状态重名 会被替换 不重名不影响 
                return {
                    text:prevState.text.toUpperCase()
                }
            }

            fun=()=>{
                this.setState({
                    text:"haha"
                })
            }
            
            render() {
                return (
                    <div>
                        <h1>新生命周期--{this.state.text}---{this.state.age}</h1>    
                        <button onClick={this.fun}>点我修改state</button>
                    </div>
                );
            }
        }

        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

getSnapshotBeforeUpdate(很少用)

  • 替换componentWillUpdate函数,将参数返回并传递给componentDidUpdate周期函数
  • getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()
  • 此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
      
            
            getSnapshotBeforeUpdate = () => {
              console.log("getSnapshotBeforeUpdate")
        
            }
          
            
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1>   
                    </div>
                );
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

加入之后会出现如下警告

原因是因为:getSnapshotBeforeUpdate()应与componentDidUpdate()一起使用。此组件仅定义getSnapshotBeforeUpdate()。所以不能单独使用需要配合其他钩子进行使用

tu16

加入componentDidUpdate 就不会出现问题了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
      
            
            getSnapshotBeforeUpdate = () => {
              console.log("getSnapshotBeforeUpdate")
        
            }
          
           componentDidUpdate = (prevProps, prevState) => {
             console.log("componentDidUpdate")
           }
           
            
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1> 
                       
                       
                    </div>
                );
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

修改数据 触发当前钩子函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            state={
                text:"我是默认值"
            }
            
            getSnapshotBeforeUpdate = () => {
              console.log("getSnapshotBeforeUpdate")
        
            }
          
           componentDidUpdate = (prevProps, prevState) => {
             console.log("componentDidUpdate")
           }
           
           fun=()=>{
            this.setState({
                text:"我修改了"
            })
           }
            
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1> 
                       <h1>{this.state.text}</h1>
                       <button onClick={this.fun}>点我修改</button>
                       
                    </div>
                );
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

但是会发现 钩子是顺利触发了 但是出现如下警告

tu16

出现问题的原因是因为 需要返回一个快照值 或者是一个null

 					getSnapshotBeforeUpdate = () => {
              console.log("getSnapshotBeforeUpdate")
            //   返回null
              return null
        
            }

返回值是null 那么这个钩子函数就没有作用了 那么什么是快照值呢? 根据概念来猜测--此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。 这个值可以在componentDidUpdate()的第三个参数接受 所以我们来试着return 传递下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>

</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
            state={
                text:"我是默认值"
            }
            
            getSnapshotBeforeUpdate = () => {
              console.log("getSnapshotBeforeUpdate")
            //   返回
              return "xixi"
        
            }
          
           componentDidUpdate = (prevProps, prevState,Snapshot) => {
             console.log("之前的props",prevProps)
             console.log("之前的state",prevState)
             console.log("Snapshot快照值",Snapshot)
           }
           
           fun=()=>{
            this.setState({
                text:"我修改了"
            })
           }
            
            render() {
                return (
                    <div>
                        <h1>新生命周期</h1> 
                       <h1>{this.state.text}</h1>
                       <button onClick={this.fun}>点我修改</button>
                       
                    </div>
                );
            }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

小练习

需求:每一秒添加一个新闻 并且在数量一定的时候 页面产生滚动。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>
    <style>
        .container{
            height: 100px;
            width: 100px;
            background-color: pink;
            overflow: auto;
        }
        .item{
            height: 30px;
        }
    </style>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
           state={
            arr:[]
           } 

        //    每一秒自动往arr中添加一条数据
           componentDidMount = () => {
            let i=0;
             setInterval(() => {
                i++
                this.setState({arr:[i,...this.state.arr]})
             }, 1000);
           }
           

           render(){
            console.log(this.state.arr)
            return (
                <div className="container">
                    {
                        this.state.arr.map((v,i)=>{
                            return (
                                <div className="item" key={i}>{v}</div>
                            )
                        })
                    }
                </div>
            )
           }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

但是每次添加新内容页面就会自动滚动 那么我如何让页面定在我们选中的位置呢?

1.给整体div添加一个ref方便进行dom查找

<div className="container" ref={(demo)=>{this.demodiv=demo}}>

2.在页面修改展示之前先得到当前这个页面整体的高度 就是类名container(scrollHeight它返回该元素的像素高度)

					    getSnapshotBeforeUpdate=()=>{
 
                return this.demodiv.scrollHeight
           }

3.其次在数据更新完成之后 让页面距离顶部的高度(scrollTop) 加等于 当前页面高度减去 更新数据之前的高度

				componentDidUpdate = (prevProps, prevState,height) => {
                this.demodiv.scrollTop+=this.demodiv.scrollHeight-height
           }	

整体代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.16.8.6.js"></script>
    <script src="./js/react-dom.16.8.6.js"></script>
    <script src="./js/babel.min.js"></script>
    <style>
        .container{
            height: 100px;
            width: 100px;
            background-color: pink;
            overflow: auto;
        }
        .item{
            height: 30px;
        }
    </style>
</head>
<body>
    <div id="demoDiv"></div>
    <script type="text/babel">
        
        class Com extends React.Component {
           state={
            arr:[]
           } 

        //    每一秒自动往arr中添加一条数据
           componentDidMount = () => {
            let i=0;
             setInterval(() => {
                i++
                this.setState({arr:[i,...this.state.arr]})
             }, 1000);
           }
           

           getSnapshotBeforeUpdate=()=>{
 
                return this.demodiv.scrollHeight
           }

           componentDidUpdate = (prevProps, prevState,height) => {
                this.demodiv.scrollTop+=this.demodiv.scrollHeight-height
           }
           
           render(){
            console.log(this.state.arr)
            return (
                <div className="container" ref={(demo)=>{this.demodiv=demo}}>
                    {
                        this.state.arr.map((v,i)=>{
                            return (
                                <div className="item" key={i}>{v}</div>
                            )
                        })
                    }
                </div>
            )
           }
        }
        ReactDOM.render(<Com/>,document.getElementById("demoDiv"))

    </script>
</body>
</html>

总结

新的生命周期去掉了三个will钩子,分别是:

componentWillMount 、componentWillReceiveProps、componentWillUpdate

新的生命周期新增了两个钩子,分别是:

1.getDerivedStateFromProps(nextProps, prevState) 用来替代 componentWillReceiveProps()。

2.getSnapshotBeforeUpdate(prevProps, prevState)方法用来替代componentWillUpdate()。

13.cra(create-react-app)

近期 create-react-app更新了

他里面对node的版本有要求了 node的版本不能低于14了

注意:win7系统node的版本不能大于12 (需要更新系统)

安装

1.全局安装create-react-app

npm install -g create-react-app

查看版本 create-react-app --version

2.cd到指定文件夹下

3.创建项目

create-react-app 项目名         your-app 注意命名方式

注意:如果不想全局安装可以使用npx来进行项目的安装(临时安装)

npx create-react-app myapp 也可以实现相同的效果

这需要等待一段时间,这个过程实际上会安装三个东西

react: react的顶级库

react-dom: 因为react有很多的运行环境,比如app端的react-native, 我们要在web上运行就使用

react-dom

react-scripts: 包含运行和打包react应用程序的所有脚本及配置

出现下面的界面,表示创建项目成功

Success! Created your-app at /dir/your-app 
Inside that directory, you can run several commands:
npm start 
Starts the development server. 
npm run build
Bundles the app into static files for production.
npm test Starts the test runner. npm run eject Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back! We suggest that you begin by typing:

cd your-app 
npm start

Happy hacking!

4.cd到你创建的项目下

5.npm start 启动项目

文件结构

生成项目的目录结构如下:

├── README.md 使用方法的文档

├── node_modules 所有的依赖安装的目录

├── package-lock.json 锁定安装时的包的版本号,保证团队的依赖能保证一致。

├── package.json

├── public 静态公共目录

└── src 开发用的源代码目录

常见问题

npm安装失败

淘宝镜像

1.切换为npm镜像为淘宝镜像

 npm config set registry https://registry.npm.taobao.org 

yarn

2.使用yarn,如果本来使用yarn还要失败,还得把yarn的源切换到国内

yarn就是和npm一样 都是一个包管理工具

yarn是由 facebook推出的一个包管理工具

yarn安装:npm install -g yarn

yarn和npm 的对照表

功能npmyarn
初始化npm inityarn init
安装依赖npm installyarn install或者 yarn
新增依赖npm install --save xxxyarn add xxx
全局安装npm install -g xxxyarn global add xxx
同时下载多个npm install --save xx1 xx2yarn add xx1 xx2
删除依赖npm uninstall --save xxxyarn remove xxx

如果觉得yarn默认下载很慢 那么我们可以把yarn切换成淘宝镜像地址

yarn config set registry https://registry.npm.taobao.org/

3.如果还没有办法解决,请删除node_modules及package-lock.json然后重新执行 npm

install命令

4.再不能解决就删除node_modules及package-lock.json的同时清除npm缓存 npm cache

clean --force 之后再执行 npm install 命令

14 样式模块化

主要防止css命名空间的污染(在vue中可以通过scoped来进行解决样式污染)那么在react中怎么解决呢?

方式1 scss样式嵌套

下载 npm install --save sass-loader@10 node-sass@6

直接编写scss文件 然后在需要使用的组件内 使用 import “你的样式文件路径即可”

方式2 CSS Module 样式模块化

1.在样式文件与.css后缀名之间 加入module 例如:style.css --- > style.module.css

2.在所需要的组件中使用 import 起个名字 from “xxx.module.css地址”

3.在使用的dom中在className上面添加 模块名.名字即可生效

15 多行html 空标签

在react的组件中多行html必须有一个父容器包裹 所以通常我们使用div来进行包裹 但是有的时候这些div是多余的 会在页面生成很多无用的代码

import React from 'react'
import ReactDOM from 'react-dom'

class Demob extends React.Component {
	render() {
		return (
            // 多行标签必须有一个父容器包裹
            <div>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
            </div>
          
        )
	}
}

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

空标签

空标签 在页面是不进行展示的 它的作用仅仅就是用来描述多行标签的一个包裹作用

写法1:

<></>

import React from 'react'
import ReactDOM from 'react-dom'

class Demob extends React.Component {
	render() {
		return (
            // 空标签
            <>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
            </>
          
        )
	}
}

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

写法2:

Fragment空标签

import React from 'react'
import ReactDOM from 'react-dom'

class Demob extends React.Component {
	render() {
		return (
			// 空标签
			<React.Fragment>
				<h1>你好我是一个标签</h1>
				<h1>你好我是一个标签</h1>
				<h1>你好我是一个标签</h1>
				<h1>你好我是一个标签</h1>
				<h1>你好我是一个标签</h1>
			</React.Fragment>

		)
	}
}

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