目录

React类组件上

React:类组件(上)

kerwin老师我来了

类组件的创建

class组件,js里的类命名首字符大写,类里面包括构造函数,方法

组件类要继承React.Component才有效

必须包含render方法

import React from 'react'
class App extends React.Component{
    render() {
        return <div>hello react Component</div>
    }
}
export default App

在index.js里引入并使用标签,就算使用一个类组件了

https://i-blog.csdnimg.cn/direct/cff66b74e1bd40c0b5e15c37ca1d52f7.png

组件返回的内容必须是封闭的,必须包括在一个完整的标签里

react16.8之前函数组件又叫无状态组件,在之后引入了hooks,函数式组件就有了状态

组件的嵌套

一个页面里只有一个根组件

声明三次组件


import React, { Component } from "react"
//export {Component},es6的模块化导出,也就是之前说过的默认导出,命名导出

//声明类组件
class Navbar extends Component {
    render() {
        return <div>navbar</div>
    }
}
//声明函数组件
function Swiper() {
    return <div>Swiper</div>
}
//箭头函数组件
const Tabbar = () => <div>Tabbar</div>

 class App extends Component{
    render(){
        return (
            <div>
                <Navbar></Navbar>
                <Swiper></Swiper>
                <Tabbar></Tabbar>
            </div>
        )
    }
}
export default App

https://i-blog.csdnimg.cn/direct/4988b4c1def049299ba7c6f8215188de.png

这样直接在子组件里再写子组件是显示不出来的,其实这种方式也是可以实现爷孙嵌套的,但是先不讲

 class App extends Component{
    render(){
        return (
            <div>
                <Navbar>
                    <Child/>
                </Navbar>
                <Swiper></Swiper>
                <Tabbar></Tabbar>
            </div>
        )
    }
}

如果想体现Child是Navbar的子组件,需要单独写一个Child,然后在Navbar里调用:


import React, { Component } from "react"
//export {Component},es6的模块化导出,也就是之前说过的默认导出,命名导出
const Child = function () {
    return <div>Child</div>
}
//声明类组件
class Navbar extends Component {
    render() {
        return <div>navbar
            <Child></Child>
        </div>
    }
}
//声明函数组件
function Swiper() {
    return <div>Swiper</div>
}
//箭头函数组件
const Tabbar = () => <div>Tabbar</div>

 class App extends Component{
    render(){
        return (
            <div>
                <Navbar>
                    <Child/>
                </Navbar>
                <Swiper></Swiper>
                <Tabbar></Tabbar>
            </div>
        )
    }
}
export default App

这样就好了,还可以体现Navbar和Child的父子关系

https://i-blog.csdnimg.cn/direct/96fd496c3292405b8ee0c7f931c43cad.png https://i-blog.csdnimg.cn/direct/0f831faa4b1d4ce58f03d7338ee23797.png

组件的样式

{}里的内容当js处理

样式的两种处理:行内样式,使用class,给dom添加className

之所以可以把外部的css等模块直接导入,免不了webpack的支持,webpack把导入的样式模块转化为内部的style样式 https://i-blog.csdnimg.cn/direct/eeda92547dd74f6b910e1c4469b29d4d.png

而脚手架把webpack封装了

jsx里,for是js的保留字,在react的现版本更推荐htmlFor

当用户点击

import React, { Component } from "react";

class App extends Component {
    render() {
        return (
            <div>
                <label htmlFor='username'>用户名</label>
                <input type='text' id='username' />
            </div>
        );
    }
}

export default App;

react里更推荐行内样式

React

推荐我们使用行内样式,因为

React

觉得每一个组件都是一个独立的整体

这个地方不能加小括号,是回调函数,加了小括号就会立马执行

   <button onClick={this.handleClick()}>add2</button>

并且这个时候onClick={},{}里面是函数的返回值,如果里面的函数加了小括号,就会执行并且把返回值赋给onClick,如果里面的函数没有返回值,就是undefined

开启this大战

import React, { Component } from "react" 
class App extends Component{

   a=100
   handleClick2() {
    console.log('onclick2',this.a)
}
  
    render(){
        return (
            
            <button onClick={this.handleClick2}>add2</button>
      
            </div>
        )
    }
}
export default App

希望可以通过点击事件回调函数的this获取App里的a

失败了孩子们 https://i-blog.csdnimg.cn/direct/ba063328c0934038863d4fee125d1f2f.png 我们来看看this是谁: https://i-blog.csdnimg.cn/direct/130249d219bc4f77a5a7502a23ec1b41.png 为什么是undefined呢孩子们,谁调用this指向谁,点击以后执行,是react的事件系统调用的,指谁也指不到App啊,a是App里的属性,所以也就获取不了a了

总之就是this指向出问题了,怎么修正this指向呢?以前我们在js里学了好几个更改this的方法,比如bind()、apply()、call()

    <button onClick={this.handleClick2.bind(this)}>add2</button>
//不推荐这种写法

成功了孩子们

https://i-blog.csdnimg.cn/direct/64f759b11c404c08a73fa983934e58e4.png

问题又来了:为什么是bind()呢?因为call和apply会自动调用函数,而回调函数不需要立马调用,且apply用于数组

如果想获取App的属性,可以直接用箭头函数:

import React, { Component } from "react" 
class App extends Component{

   a=100
   handleClick2() {
    console.log('onclick2',this.a)
}
    handleClick3 = () => {
    console.log('onclick3',this.a)
    }
    render(){
        return (
            <div>
                <input type='text' id='username' />
            <button onClick={this.handleClick3}>add3</button>
//比较推荐这种写法
            </div>
        )
    }
}
export default App

因为箭头函数会引用上个作用域的this,在这里就是App,所以可以打印App的属性

https://i-blog.csdnimg.cn/direct/80509175a9cf4556a0a5703573110da5.png

在里面也使用箭头函数和this调用

       <button onClick={() => {
                    this.handleClick4()
                    //可以传参,比较推荐
            }}>add4</button>
       handleClick4 = () => {
    console.log('onclick4',this.a)
}

里面的this和外面的this是一样的

https://i-blog.csdnimg.cn/direct/a9edbf3001734b248ac56eedb0e353ab.png

事件处理

React并不会真正的绑定事件到每个具体的元素上,而是采用事件代理的模式

React 在应用的根节点(通常是 #root#app )上监听所有支持的事件(如 click、 change 等)事件发生时,React 会根据事件的 target (触发事件的元素)找到对应的组件,并调用组件中定义的事件处理函数,通常在冒泡阶段处理事件,也就是点击按钮时,事件冒泡到根节点,React 调用 handleClick执行

优点是性能优化,减少内存占用、封装了浏览器的事件系统、在组件卸载时自动移除事件监听器,避免内存泄漏

关于事件对象e

和普通浏览器一样,事件handler

会被自动传入一个

event

对象,这个对象和普通的浏览器

event

对 象所包含的方法和属性都基本一致。不同的是 React

中的

event 对象并不是浏览器提供的,而是它自己内部所构建的,也就是一个合成事件e, 它是 React 对浏览器原生事件对象的封装 。它同样具有 event.stopPropagation 、

event.preventDefault

这种常用的方法

ref

ref是引用的意思

用一种古旧的方法(react目前已不支持refs访问dom元素或组件了)获取标签:

import React, { Component } from "react" 
class App extends Component{

   a=100

    render(){
        return (
            <div>
                <input ref='mytext' />
                <button onClick={()=>{console.log('click1',this.refs.mytext)
                }}>add1</button>
            </div>
        )
    }
}
export default App

这就代表我们获取到这个元素了,获取这个元素以后也可以获取他的属性

https://i-blog.csdnimg.cn/direct/4448c00732db4b3a961fd55c886f1dcc.png

新的写法是这么写的:

https://i-blog.csdnimg.cn/direct/70f63ea8f6604ed9aa94d59686b2b9d2.png

import React, { Component } from "react" 
class App extends Component{

    a=100
    myref=React.createRef()//返回一个ref对象
    render(){
        return (
            <div>
                <input ref='mytext' />
                <input ref={this.myref} />
                {/* //把ref绑定在input上 */}
                <button onClick={() => {
                    console.log('click', this.myref)
                }}>add1</button>
            </div>
        )
    }
}
export default App

https://i-blog.csdnimg.cn/direct/dd2bbaa040fb49de8dd6e58f67af54ae.png

获取到的是一个对象,原生dom节点被放在ref的current属性里

https://i-blog.csdnimg.cn/direct/927089c233cd48299b405769c3e7852e.png

注意在类组件里要一直注意this指向的问题

状态

在我还没有学react的时候,我以为这样就可以切换按钮内容了

import React, { Component } from "react" 

class App extends Component{
render() {
        let text = '收藏'
        return (
            <div>        
                <button onClick={()=>text = '取消收藏'}>{text}</button>
            </div>
        )
    }
}
export default App

实则不然,在react内部并不知道你改变了它,不会渲染,需要用状态来管理

React里我们要尽量减少对dom的操作,因为react内部已经在做dom操作了,你只需要告诉他你要什么

状态可以满足我们不改变dom,通过数据来操作页面的要求

import React, { Component } from "react" 

class App extends Component{
 state = {
     show:true
       }
    render() {
       
        return (
            <div>
                <button onClick={() => { 
                    this.setState({
                        show:!this.state.show
                    })
                }}>{ this.state.show?'收藏':'取消收藏'}</button> 
            </div>
        )
    }
}
export default App

类组件更像是在用面向对象的思想编程

除了这种写法还有这样设置状态的写法

constructor() {
super()
this.state = {
name: 'React',
isLiked: false
}
}

super() 用于调用父类( React.Component )的构造函数,保证this正确指向组件实例

列表渲染

写写类组件的列表渲染

import React, { Component } from "react" 

class App extends Component{
    state = {
        list: [
            {
            id:1,text:'111'
            },
            { id: 2, text: '222' },
            {id:3,text:'333'}
    ]
}
    render() {
     
        return (
            <div>
                <ul>
                    {this.state.list.map((item) => <li key={item.id}>{ item.text}</li>)}
               </ul>
            </div>
        )
    }
}
export default App

kerwin老师对key的解析:

Diff 算法 是 React 用于比较虚拟 DOM(Virtual DOM)更新前后差异的核心算法。它的目的是高效地计算出需要更新的部分,从而最小化对真实 DOM 的操作,提升性能。

https://i-blog.csdnimg.cn/direct/3b7f94754c874109938b4b27af0c41ad.png

对于相同的元素类型,React 会保留该 DOM 节点,只更新变化的属性。

对于不同的元素类型,React 会销毁旧节点并创建新节点。

Key :为列表中的每个元素提供唯一的 key ,帮助 React 识别元素的移动、添加或删除,为了列表的复用和重排,设置key值,提高性能

理想的key是数组里的对象,item.id是对象里的一个属性(最好不要拿数组下标,这样删除一个后面的都会乱,不涉及列表的增加、删除、重排是可以设置为索引值的)

https://i-blog.csdnimg.cn/direct/5c51257485884b1288995ffe0f14f518.png

tolist制作

添加数据

对于状态的控制和函数组件的useState是相似的,得把更改的结果传入:

this.state.list.push(this.myref.current.value)不能直接修改状态

为了避免直接修改状态,我们可以复制一份修改,然后把新的值赋值回去:

     let newList = this.state.list//这样对吗?

显然是不对的,这叫赋值,赋值的本质二者指向的还是同一块内存地址

https://i-blog.csdnimg.cn/direct/9a745cbdd5e643e28a5722d4864cd48e.png

这时候其实还是相当于直接修改了状态,所以我们要进行拷贝(深拷贝和浅拷贝)

类组件实现的todolist

import React, { Component } from "react" 
class App extends Component{

    a=100
    myref = React.createRef()//返回一个ref对象
        state = {
        list: [
            {
            id:1,text:'111'
            },
            { id: 2, text: '222' },
            {id:3,text:'333'}
    ]
}
    render(){
        return (
            <div>
                <input ref={this.myref} />
                {/* //把ref绑定在input上 */}
                <button onClick={this.handleClick}>add</button>
                <ul>
                    {
                        this.state.list.map((item) => <li key={item.id}>{ item.text}</li>)
}
                </ul>
            </div>
        )
    }
    handleClick = () => {
        console.log('click', this.myref.current.value)
        //this.state.list.push(this.myref.current.value)不能直接修改状态
        let newList = [...this.state.list]
        newList.push({
            id:this.state.list.length+1,
            text:this.myref.current.value
        })
        this.setState({
            list:newList
        })
    }
}
export default App

删除数据

用bind绑定删除的元素index

import React, { Component } from "react" 
class App extends Component{

    a=100
    myref = React.createRef()//返回一个ref对象
        state = {
        list: [
            {
            id:1,text:'111'
            },
            { id: 2, text: '222' },
            {id:3,text:'333'}
    ]
}
    render(){
        return (
            <div>
                <input ref={this.myref} />
                {/* //把ref绑定在input上 */}
                <button onClick={this.handleClick}>add</button>
                <ul>
                    {
                        this.state.list.map((item,index) => <li key={item.id}>{ item.text}<button onClick={this.handleDel.bind(this,index)}>delete</button></li>)
}
                </ul>
            </div>
        )
    }
    handleClick = () => {
        console.log('click', this.myref.current.value)
        //this.state.list.push(this.myref.current.value)不能直接修改状态
        let newList = [...this.state.list]
        newList.push({
            id:this.state.list.length+1,
            text:this.myref.current.value
        })
        this.setState({
            list:newList
        })
    }
    handleDel = (index) => {
        console.log('del',index)
        
    }
}
export default App

其实我决定根据id删除才是更正确的方式

https://i-blog.csdnimg.cn/direct/06101fa515104ee5a7c37d911af0f1be.png

import React, { Component } from "react" 
class App extends Component{

    a=100
    myref = React.createRef()//返回一个ref对象
        state = {
        list: [
            {
            id:1,text:'111'
            },
            { id: 2, text: '222' },
            {id:3,text:'333'}
    ]
}
    render(){
        return (
            <div>
                <input ref={this.myref} />
                {/* //把ref绑定在input上 */}
                <button onClick={this.handleClick}>add</button>
                <ul>
                    {
                        this.state.list.map((item,index) => <li key={item.id}>{ item.text}<button onClick={()=>this.handleDel(index)}>delete</button></li>)
}/*{传递参数让你知道你删除的是第几个}*/
                </ul>
            </div>
        )
    }
    handleClick = () => {
        console.log('click', this.myref.current.value)
        //this.state.list.push(this.myref.current.value)不能直接修改状态
        let newList = [...this.state.list]
        newList.push({
            id:this.state.list.length+1,
            text:this.myref.current.value
        })
        this.setState({
            list:newList
        })
    }
    handleDel = (index) => {
        console.log('del',index)
        let newList =this.state.list.slice()//复制
        newList.splice(index, 1)//删除
        this.setState(//修改状态
            {list:newList}
        )
    }
}
export default App

https://i-blog.csdnimg.cn/direct/172814b8c56d47849a1ee444af2c0553.png

条件渲染

根据条件渲染

增加一些小功能:清除input,在数组长度为0时显示todolist空空如也

点击按钮控制受控组件的value为0

    handleClick = () => {
        console.log('click', this.myref.current.value)
        //this.state.list.push(this.myref.current.value)不能直接修改状态
        let newList = [...this.state.list]
        newList.push({
            id:this.state.list.length+1,
            text:this.myref.current.value
        })
        this.setState({
            list:newList
        })
        this.myref.current.value=''
    }

数组长度为0时,显示暂无待办事项

//三目运算符
   { this.state.list.length?null:<div>暂无待办事项</div>}
//逻辑短路
 this.state.list.length===0&&<div>暂无待办事项</div>}

https://i-blog.csdnimg.cn/direct/aaa78e3558b748a7b427156e50d94cf3.png

还有一种方法是让他不满足条件时隐藏,满足时显示,要引入css

 <div className={this.state.list.length===0?'':'hidden'}>暂无待办事项</div>

.hidden{
     display:none;
 }