react(三)-- 类组件

component - class

  1. 知识回顾(类的继承)
    01 super(props) super有参数
		class A{
            constructor(n){
                //this指向B的实例
                this.x = n
            }
        }
        class B extends A{
            constructor(props){
                super(props)
                //给B实例添加私有属性
                this.y = 100
            }
        }
        let b = new B(100)
        console.log(b.x);
        console.dir(b); 

在这里插入图片描述
        02 super( )无参数时
在这里插入图片描述

  1. 定义类组件:有自己的状态 和 生命周期
<!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>
</head>
<body>
    <!-- 挂载容器 -->
    <div id="app"></div>
    <!-- 引入库 -->
    <script src="../js/react.development.js"></script>
    <script src="../js/react-dom.development.js"></script>
    <script src="../js/babel.min.js"></script>

    <script type="text/babel">
        class Welcome extends React.Component{
            constructor(props){
                console.log(props); //{name:"tina"}
                super(props) //调用super时,该类组件的父类会执行this.props = props
            }
            //rend必须写,渲染时会自动被调用  组件内容,通过返回值返回
            render(){
                console.log(this); //this指向当前类的实例
                console.log(this.props);//{name:"tina"}
                return <h1>Hello World!</h1>
            }
        }
        const e = <Welcome name = "tina" />
        console.log(e);
        //04 虚拟DOM 伪代码
        // {
        //     type:f Welcome(props),
        //     props:{name:'tina'}
        // }

        //虚拟DOM => type判断(如果是类组件,new调用new Welcome(props))=>真实DOM => 渲染到容器里面
        ReactDOM.render(e,document.getElementById("app"));
    </script>
</body>
</html>

在这里插入图片描述
注意:

即使不在constructor中调用super(props),render中依然可以使用this.props,因为render可以帮我们做;
那么,在constructor中调用super(props)有什么意义呢?
在constructor中调用super(props),可以在constructor阶段直接使用this.props
例如:super()不传参数,在constructor阶段中打印this.props结果为undefined

  1. 时钟案例
    01 函数组件(手动)
		//函数组件:时钟
        function Clock(props){
            return (
                <div>
                    <h1>Clock</h1>
                    <h2>{props.time}</h2>
                </div>
            )
        }
        function tick(){
            var e = <Clock time={new Date().toLocaleTimeString()}/>
            ReactDOM.render(e,document.getElementById("app"));
        }
        //手动:每隔一秒钟 波动一次秒针
        setInterval(tick,1000)

        02 函数组件转化为类组件(手动)

		class Clock extends React.Component{
            render(){
                console.log(this.props);
                return(
                    <div>
                        <h1>Clock</h1>
                        <h2>{this.props.time}</h2>
                    </div>
                )   
            }
        }
        function tick(){
            var e = <Clock time={new Date().toLocaleTimeString()}/>
            ReactDOM.render(e,document.getElementById("app"));
        }
        //手动:每隔一秒钟 波动一次秒针
        setInterval(tick,1000)

注意:

当我们在render中打印this.props的时候,会发现它会一直重复打印,这是为什么呢?
——这是因为render具有该特点: 每次props变化,render会重新执行,页面会重新渲染;

        03 自动化时钟(维护状态state)

        当我们在componentDidMount(){}钩子函数中,用this.state.time=""改变数据时,会发现,打印出的数据被成功修改,但是页面并没有更新,这是react不同于vue之处,它不具备双向绑定。
解决方法:强制更新
(1)使用forceUpdate()强制刷新,每次刷新都会重新执行
(2)使用this.setState()也会重新执行(推荐使用

	class Clock extends React.Component{
        constructor(props){
            super(props)
            this.state = {
                time : new Date().toLocaleTimeString(),
                //n:10
            }    
        }
        render(){
            return(
                <div>
                    <h1>Clock</h1>
                    <h2>{this.state.time}</h2>
                    //点击按钮销毁div(app)
                    <button onClick={
                        ()=>{
                            ReactDOM.unmountComponentAtNode(document.getElementById('app'))
                        }
                    }>remove Component</button>
                       // <h2>{this.state.n}</h2>
                </div>
            )
        }
        //第一次渲染完毕,会执行该钩子函数
        componentDidMount(){
            console.log('componentDidMount');
            // this.state.time='abc'
            // console.log(this.state);
            // this.forceUpdate()
            this.timerId=setInterval(()=>{
                this.setState({time:new Date().toLocaleTimeString()})
            },1000)
            //修改n,发现并未改变,因为setState 在这时是异步的
            //解决方法:回调函数
            // this.setState({
            //    n:13
            // },()=>{
                //     console.log("callback",this.state);
                // })
                //console.log(n);
        }
        //第一次渲染完毕,会执行该钩子函数
        componentWillUnmount(){
            clearInterval(this.timerId)
        }
    }
    ReactDOM.render(<Clock />,document.getElementById("app"));
  1. 类组件的this指向问题
    01 生命周期中的钩子函数中的this - -> 指向当前类组件的实例(如:componentDidMount,render)
		class Clock extends React.Component{
            constructor(props){
                super(props)
                this.state = {
                    time : new Date().toLocaleTimeString(),
                }
                
            }
            render(){
                return(
                    <div>
                        <h1>Clock</h1>
                        <h2>{this.state.time}</h2>
                        <button onClick={
                            ()=>{
                            ReactDOM.unmountComponentAtNode(document.getElementById('app'))
                            }
                        }>remove Component</button>
                       
                    </div>
                )
            }

            componentDidMount(){
                console.log('componentDidMount');
                console.log(this);
                this.timerId=setInterval(()=>{
                	this.setState({time:new Date().toLocaleTimeString()
                    })
                },1000)
                
            }
            
            componentWillUnmount(){
                clearInterval(this.timerId)
            }
        }
        ReactDOM.render(<Clock />,document.getElementById("app"));

在这里插入图片描述
        02 函数的this --> 指向undefined

        class Clock extends React.Component{
            constructor(props){
                super(props)
                this.state = {
                    time : new Date().toLocaleTimeString(),
                }
                
            }

            go(){
                console.log(this); //undefined
                this.setState({
                    time : new Date().toLocaleTimeString(),
                })
            }

            render(){
                return(
                    <div>
                        <h1>Clock</h1>
                        <h2>{this.state.time}</h2>
                        <button onClick={this.go}>GO1</button>                        
                        <button onClick={
                            ()=>{
                            ReactDOM.unmountComponentAtNode(document.getElementById('app'))
                            }
                        }>remove Component</button>
                       
                    </div>
                )
            }

            componentDidMount(){
                console.log('componentDidMount');
            }
            
            componentWillUnmount(){
                clearInterval(this.timerId)
            }
        }
        ReactDOM.render(<Clock />,document.getElementById("app"));

在这里插入图片描述
        03 this.go 与 this.go()

this.go --> 表示引用 (this指向undefined);
this.go()–> 找到函数,直接执行(报错,会导致内存溢出<–重复调用render和执行函数–死循环,this指向调用者);

		class Clock extends React.Component{
            constructor(props){
                super(props)
                this.state = {
                    time : new Date().toLocaleTimeString(),
                }
            }

            go(){
                console.log(this); 
                this.setState({
                    time : new Date().toLocaleTimeString(),
                })
            }

            render(){
                return(
                    <div>
                        <h1>Clock</h1>
                        <h2>{this.state.time}</h2>
                        <button onClick={this.go()}>GO2</button>                        
                        <button onClick={
                            ()=>{
                            ReactDOM.unmountComponentAtNode(document.getElementById('app'))
                            }
                        }>remove Component</button>
                       
                    </div>
                )
            }

            componentDidMount(){
            
            }
            
            componentWillUnmount(){
                clearInterval(this.timerId)
            }
        }
        ReactDOM.render(<Clock />,document.getElementById("app"));

在这里插入图片描述
        04 关于函数this指向问题的解决方法
        (1)使用箭头函数:箭头函数中的this指向该箭头函数所在的外围作用域,即render,render --> 指向当前类组件的实例;
        *问题:

  • 函数是一个特殊对象,需要占用内存空间;每次调用render()都i相当于重复声明 => 重复开辟内存空间,性能相对较差;
  • jsx元素构建中添加js逻辑代码,导致代码复杂、臃肿,不好维护
        class Clock extends React.Component{
            constructor(props){
                super(props)
                this.state = {
                    time : new Date().toLocaleTimeString(),
                }
            }
            render(){
                return(
                    <div>
                        <h1>Clock</h1>
                        <h2>{this.state.time}</h2>
                        <button onClick={
                            ()=>{
                                this.setState({
                                    time : new Date().toLocaleTimeString(),
                                })
                            }
                        }>GO3</button>
                                              
                        <button onClick={
                            ()=>{
                            ReactDOM.unmountComponentAtNode(document.getElementById('app'))
                            }
                        }>remove Component</button>
                       
                    </div>
                )
            }

            componentDidMount(){                
            }
            
            componentWillUnmount(){
                clearInterval(this.timerId)
            }
        }
        ReactDOM.render(<Clock />,document.getElementById("app"));

        (2)手动改变this指向,使用bind进行绑定;
        *注意:该方法虽然解决了代码逻辑混乱问题,但是每次重新绑定都会生成新的函数,依然没有解决重复开辟内存空间,性能相对较差的问题

        class Clock extends React.Component{
            constructor(props){
                super(props)
                this.state = {
                    time : new Date().toLocaleTimeString(),
                }
            }

            go(){
                console.log(this); //指向当前类组件的实例
                this.setState({
                    time : new Date().toLocaleTimeString(),
                })
            }

            render(){
                return(
                    <div>
                        <h1>Clock</h1>
                        <h2>{this.state.time}</h2>
                        <button onClick={this.go.bind(this)}>GO4</button>
                                              
                        <button onClick={
                            ()=>{
                            ReactDOM.unmountComponentAtNode(document.getElementById('app'))
                            }
                        }>remove Component</button>
                       
                    </div>
                )
            }

            componentDidMount(){
                console.log('componentDidMount');
            }
            
            componentWillUnmount(){
                clearInterval(this.timerId)
            }
        }
        ReactDOM.render(<Clock />,document.getElementById("app"));

在这里插入图片描述
        (3)推荐方式:在constructor构造函数中,一次性绑定this,在render中直接通过this.go进行调用

		class Clock extends React.Component{
            constructor(props){
                super(props)
                this.state = {time : new Date().toLocaleTimeString()}    
                //一次性绑定this
                console.log(this);
                //go函数 => 生成新的函数(新的函数已经绑定this) => 新的函数赋值给this.go属性上
                this.go = this.go.bind(this)
            }

            go(){
                console.log("GO5");
                this.setState({
                    time : new Date().toLocaleTimeString(),
                })
            }

            render(){
                return(
                    <div>
                        <h1>Clock</h1>
                        <h2>{this.state.time}</h2>
                        <button onClick={this.go}>GO5</button>
                                              
                        <button onClick={
                            ()=>{
                            ReactDOM.unmountComponentAtNode(document.getElementById('app'))
                            }
                        }>remove Component</button>
                       
                    </div>
                )
            }

            componentDidMount(){
                console.log('componentDidMount');
            }
            
            componentWillUnmount(){
                clearInterval(this.timerId)
            }
        }
        ReactDOM.render(<Clock />,document.getElementById("app"));

在这里插入图片描述
        (4)ES7新的函数写法go=()=>{},不需要考虑this , 可以在render中直接通过this.go进行调用

        class Clock extends React.Component{
            constructor(props){
                super(props)
                this.state = {time : new Date().toLocaleTimeString()}    
                //一次性绑定this
                console.log(this);
                //go函数 => 生成新的函数(新的函数已经绑定this) => 新的函数赋值给this.go属性上
                this.go = this.go.bind(this)
            }

            go=()=>{
                console.log(this);
                this.setState({
                    time : new Date().toLocaleTimeString(),
                })
            }

            render(){
                return(
                    <div>
                        <h1>Clock</h1>
                        <h2>{this.state.time}</h2>
                        <button onClick={this.go}>GO6</button>
                                              
                        <button onClick={
                            ()=>{
                            ReactDOM.unmountComponentAtNode(document.getElementById('app'))
                            }
                        }>remove Component</button>
                       
                    </div>
                )
            }

            componentDidMount(){
                console.log('componentDidMount');
            }
            
            componentWillUnmount(){
                clearInterval(this.timerId)
            }
        }
        ReactDOM.render(<Clock />,document.getElementById("app"));

在这里插入图片描述

  1. setState方法
    01 状态初始化的两种方式
	<script type="text/babel">
        class App extends React.Component{
            constructor(props){
                super(props)
                //状态初始化
                this.state = {a:0}
            }
            //状态初始化
            state1 = {b:0,c:0}
            //必须实现
            render(){
                return (
                    <div>
                        <h1>a:{this.state.a}</h1>
                        <h1>b:{this.state1.b}</h1>
                        <h1>c:{this.state1.c}</h1>
                    </div>
                )
            }
        }
        ReactDOM.render(<App />,document.getElementById("app"));
    </script>

在这里插入图片描述

        02 可以直接修改数据,但是不能触发重新渲染render

	<script type="text/babel">
        class App extends React.Component{
            //状态初始化
            state = {a:0,b:0,c:0}

            handleClick=()=>{
                //直接修改数据
                this.state.a=1
                console.log(this.state.a);
            }
            //必须实现
            render(){
                return (
                    <div>
                        <h3>a:{this.state.a}</h3>
                        <h3>b:{this.state.b}</h3>
                        <h3>c:{this.state.c}</h3>
                        <button onClick={this.handleClick}>按钮</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<App />,document.getElementById("app"));
    </script>

在这里插入图片描述
        03 setState()修改数据 + 触发render

    <script type="text/babel">
        class App extends React.Component{
            //状态初始化
            state = {a:0,b:0,c:0}

            handleClick=()=>{
                //setState()修改数据 + 触发render
                this.setState({
                    a:1
                })
                console.log(this.state.a);
            }
            //必须实现
            render(){
                return (
                    <div>
                        <h3>a:{this.state.a}</h3>
                        <h3>b:{this.state.b}</h3>
                        <h3>c:{this.state.c}</h3>
                        <button onClick={this.handleClick}>按钮</button>
                    </div>
                )
            }
            //第一次挂载完毕
            componentDidMount(){
                //生命周期钩子函数里,setState是异步的
                this.setState({
                    b:1
                })
                console.log(this.state.b);
            }
        }
        ReactDOM.render(<App />,document.getElementById("app"));
    </script>

![在这里插入图片描述](https://img-blog.csdnimg.cn/470843a318e74592985a65364f45fc5f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAYmFsYW5jZX5-,size_20,color_FFFFFF,t_70,g_se,x_16
  注意:

  • 当我们打开页面的时候,页面中的b直接变成了1,打印的结果是0,这是因为setState在生命周期钩子函数中是异步的,先执行console.log(),再执行this.setState();
  • 当我们第一次点击按钮的时候,会发现页面中a变成了1,而打印时却是0,这是因为setState在合成事件(handleClick)里面是异步的
  • 总结:
    setState是异步的条件:1)在合成事件中     2)在生命周期钩子函数中

        04 多次调用setState

	<script type="text/babel">
        class App extends React.Component{
            //状态初始化
            state = {a:0,b:0,c:0}

            handleClick=()=>{
                this.setState({a:1})
                this.setState({b:1})
                this.setState({c:1})
                console.log(this.state);
            }
            //必须实现
            render(){
            	console.log('render');
                return (
                    <div>
                        <h3>a:{this.state.a}</h3>
                        <h3>b:{this.state.b}</h3>
                        <h3>c:{this.state.c}</h3>
                        <button onClick={this.handleClick}>按钮</button>
                    </div>
                )
            }
            //第一次挂载完毕
            componentDidMount(){
               
            }
        }
        ReactDOM.render(<App />,document.getElementById("app"));
    </script>

在这里插入图片描述

  • 打印this.state结果为{a: 0, b: 0, c: 0},因为setState是异步的
  • 点击按钮后,三次setState,只打印一次render <=react性能优化 state数据全部被修改,(批量更新)
  • react性能优化,批量更新的时机是在render之前的某个时候,setState都已经更新完才会打印render

        05 累加

<script type="text/babel">
    class App extends React.Component{
        //状态初始化
        state = {a:0,b:0,c:0}

        handleClick=()=>{
            this.setState({a:1}) //a:1 不立即执行
            this.setState({a:this.state.a+1})//a:0+1=1 不立即执行
            this.setState({a:this.state.a+1})//a:0+1=1 不立即执行
        }
        //必须实现
        render(){
            console.log('render');
            return (
                <div>
                    <h3>a:{this.state.a}</h3>
                    <h3>b:{this.state.b}</h3>
                    <h3>c:{this.state.c}</h3>
                    <button onClick={this.handleClick}>按钮</button>
                </div>
            )
        }
        //第一次挂载完毕
        componentDidMount(){
               
        }
    }
    ReactDOM.render(<App />,document.getElementById("app"));
</script>

在这里插入图片描述

  • 点击按钮后,页面中a变成了1,并没有进行多次累加=>因为setState是异步的,不是立即执行的;
  • 执行机制如下:
    this.setState({a:1}) //a:1 不立即执行
    this.setState({a:this.state.a+1})//a:0+1=1 不立即执行
    this.setState({a:this.state.a+1})//a:0+1=1 不立即执行
    //发现都为a:1,所以进行合并,转换为
    this.setState({a:1})
    因此最后a为1
  • 隐患:调用setState更新的值,依赖于state里面的值,不能依赖于上一个状态去计算下一个状态

        06 解决05的累加隐患问题

<script type="text/babel">
    class App extends React.Component{
        //状态初始化
        state = {a:0,b:0,c:0}
        handleClick=()=>{
            this.setState((preState,props)=>{return{a:preState.a + 1}})
            //简写
            this.setState((preState,props)=>{a:preState.a + 1})
            this.setState((preState,props)=>{a:preState.a + 1})
            this.setState((preState,props)=>{a:preState.a + 1})
        }
        //必须实现
        render(){
            console.log('render');
            return (
                <div>
                    <h3>a:{this.state.a}</h3>
                    <h3>b:{this.state.b}</h3>
                    <h3>c:{this.state.c}</h3>
                    <button onClick={this.handleClick}>按钮</button>
                </div>
            )
        }
        //第一次挂载完毕
        componentDidMount(){
        }
    }
    ReactDOM.render(<App />,document.getElementById("app"));
</script>

在这里插入图片描述

  • setState 接收一个函数
    - 函数的第一个参数preState=>代表上一个状态
    - 函数的返回值也是一个对象,是新的状态,可以依赖于上一个状态做计算
  • 并未解决异步问题
    点击按钮a由之前的0,变成了1,再次点击变成2,只实现了依赖上一次的结果计算;而代码中有四个setState,并没有累加4,因此没有解决异步问题

        07 解决setState在合成事件中的异步问题

<script type="text/babel">
    class App extends React.Component{
        //状态初始化
        state = {a:0,b:0,c:0}

        handleClick=()=>{
            this.setState((preState,props)=>({a:preState.a + 1}),()=>{
                console.log('callback',this.state.a);
            })              
        }
        //必须实现
        render(){
            console.log('render');
            return (
                <div>
                    <h3>a:{this.state.a}</h3>
                    <h3>b:{this.state.b}</h3>
                    <h3>c:{this.state.c}</h3>
                    <button onClick={this.handleClick}>按钮</button>
                </div>
            )
        }
        //第一次挂载完毕
        componentDidMount(){
        }
    }
    ReactDOM.render(<App />,document.getElementById("app"));
</script>

this.setState((preState,props)=>({a:preState.a + 1}),()=>{
console.log(‘callback’,this.state.a);
})
通过第二个参数,通过回调函数获取state的真实值

在这里插入图片描述
        08 定时器中的setState是同步的

<script type="text/babel">
    class App extends React.Component{
        //状态初始化
        state = {a:0,b:0,c:0}

        handleClick=()=>{
            console.log(this); //箭头函数=>this指向handleClick作用域,this指向组件实例
            setTimeout(()=>{
                this.setState({a:1})                       
                this.setState({a:this.state.a+1})
                this.setState({a:this.state.a+1})                    
            },1000)
        }
        //必须实现
        render(){
            console.log('render');
            return (
                <div>
                    <h3>a:{this.state.a}</h3>
                    <h3>b:{this.state.b}</h3>
                    <h3>c:{this.state.c}</h3>
                    <button onClick={this.handleClick}>按钮</button>
                </div>
            )
        }           
    }
    ReactDOM.render(<App />,document.getElementById("app"));
</script>
  • 点击一次按钮,a变成了3,可见定时器中的setState是同步的,并且不会发生合并
  • 每次render都会执行,不会批量更新,没有状态依赖问题

在这里插入图片描述

  1. 异步问题
    01 同步
function add(a,b){
    return a + b
}
console.log(add(1,1));

        02 含有异步代码

function sum(a,b){
    //setTimeout是异步,先不执行;执行函数默认返回值undefined
    setTimeout(()=>{
        return a + b
    },1000)
    // return undefined 
}
console.log(sum(1,1));

        03 解决异步问题(1)callback

  • 回调函数callback(在函数的参数中传递一个函数,并在指定的时机去执行)
  • 问题:会导致回调地狱,不停的嵌套回调函数
function sumer(a,b,callback){    
    setTimeout(()=>{
        let result = a + b
        callback(result)
        // return a + b
    },1000)            
}
function callback(result){
    console.log(result); 
}
// sumer(1,2,callback)
let r = sumer(1,2,callback);
console.log(r);//返回值没有意义

在这里插入图片描述
        04 解决异步问题(2)封装promise–>解决了回调地狱的问题

function sumers(a,b){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            let result = a + b
            resolve(result)
        },1000)
    })
}
sumers(10,10)
.then((result)=>{
    console.log(result);
})
.catch((err)=>{
    console.log(err);
})

在这里插入图片描述

  1. 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>
</head>
<body>
    <!-- 挂载容器 -->
    <div id="app"></div>
    <!-- 引入库 -->
    <script src="../js/react.development.js"></script>
    <script src="../js/react-dom.development.js"></script>
    <script src="../js/babel.min.js"></script>

    <script type="text/babel">
        
        class App extends React.Component{
            constructor(props){
                super(props)
                this.state = {name:'zhangsan',age:18}
                this.state1 = {name:'zhangsan',age:18}
            }
            handleClick1 = ()=>{
                this.setState1({name:'lisi'})
                
            }
            setState1 = (newState)=>{
                //1.修改数据
                //{}=>{name:'zhangsan',age:18} + {name:'lisi'} => {name:'lisi',age:18}
                //Object.assign(target,...source) 源对象=>目标对象
                //(1)合并数据
                let obj = Object.assign({},this.state1,newState) //{name:'lisi',age:18}
                //(2)替换属性
                this.state1 = obj
                //2.重新渲染
                this.forceUpdate()
            }

            render(){
                return(
                    <div>
                        <h2>state name:{this.state.name}</h2>
                        <h2>state1 name:{this.state1.name}</h2>
                        <button onClick={this.handleClick1}>button1</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<App />,document.getElementById("app"));
    </script>
</body>
</html>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值