锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

react学习日记

时间:2022-11-23 00:00:00 d142对射式光电传感器

react

react-hello world

第一个react程序hello world

     

虚拟DOM创建方式

react虚拟DOM有两种创作方式:

第一:创建单级虚拟:DOM

let vDom=

hello world

二是创建有子节点的虚拟DOM

let vDom=(     

你好!世界

)

认识虚拟DOM

虚拟打印在控制台上DOM

可见虚拟DOM是个一般的object对象的属性会比真实的更真实DOM多,因为虚拟DOM被react所用,不需要太多属性,虚拟DOM最后也会被渲染成真实DOM。

虚拟DOM注释

在{}内部使用{}包裹需要注释的代码js注释

let vDom=(     

{/*你好!世界*/}

)

JSX语法糖

创建虚拟DOM不能使用引号

let vDom=(     

你好!世界

)

虚拟DOM混入js大括号需要代码:{js代码}

虚拟DOM添加class属性名需要使用className

         

虚拟DOM使用内联风格需要将风格定义为对象,{ {key:value}}:key采用小驼峰命名规则,value是字符串

{ backgroundColor:'blue',height:100 'px', width:200 'px'}}>

虚拟DOM只有一个标签,每个标签必须有一个结束标签,包括单个标签。

虚拟DOM当首字符是小写时,react它将被渲染成相应的html同名标签,如果没有相应的同名标签,就会报错;虚拟DOM首字母大写时,react将渲染其对应的组件,如果不定义相应的组将,将报告错误。

react面向组件编程

组件编程:将页面分成功能模块,每个功能模块表示组件。每个组件都有自己的html(虚拟DOM),css以及js代码。(包括页面部分功能的所有代码和资源集合,即组件)

函数组件:适用于简单组件

函数组件,定义函数,首字母大写,返回虚拟DOM,通过render函数通过组件将其函数渲染到页面上。

注:函数组件this指向的是underfined,因为react开启严格模式。

注:函数组件不能使用:state和refs,但是可以用props

使用函数组件props

提示:先看类组件props这里有相关知识

函数组件的属性将以参数的形式传输到函数组件的内部,相关约束不能在组件内部定义,只能在外部定义

类组件:适用于复杂组件

需要继承React.Component类


类组件中的this指向类的实例对象

类组件实例的三大属性

state

state:类组件实例的状态管理器,用于存放类中的状态(变量)

借助构造器初始化state

在构造器中,通过this.state设置类组件的状态,状态默认是null,在老版本的react默认是一个空对象。

小拓展:react事件绑定

react将原始的JavaScript事件进行一层封装,其名称为小驼峰命名法与原生的JavaScript进行区别

例如onclick封装成onClick

class MyComponent extends React.Component {
	constructor(props) {
		super(props)
		this.state = {
				gender: 0
			}
		this.changeGender = this.changeGender.bind(this)
		}
	changeGender() {
		console.log("aa",this);
		}
	render() {
		return (
			

//会自启动一次 你好!你的性别是{this.state.gender == 0 ? '女' : '男'}

) } }

以上代码的方法有两个问题:

问题一:方法会在页面启动的时候执行一次,原因是onClick={this.changeGender()}在{...}内的代码相当于JavaScript代码,this.changeGender()表示的是调用该函数。

需要改成:

问题二:函数changeGender的this指向underfined,原因是因为在类组件中定义方法,在方法内部开启了严格模式,所以会指向underf

解决:初始化时,通过bind将this指向类组件的实例

constructor(props) {
    super(props)
    this.changeGender = this.changeGender.bind(this)
}

setState

react是不允许直接修改state,而是需要使用setState来修改状态

class MyComponent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {gender: 0}
        this.changeGender = this.changeGender.bind(this)
        }
        changeGender() {
            this.setState({gender: !this.state.gender})
        }
        render() {
            return (
                

你好!你的性别是{this.state.gender == 0 ? '女' : '男'}

) } }

注意:setState不会覆盖state而是通过合并的方式修改state,如果没有该值,将会添加到state但是无法响应到页面。

类组件简写

state可以写在类的构造函数外面,自定义的方法通过箭头函数的方式可以实现this查找

class MyComponent extends React.Component {
	state = {gender: 0}//提取出来
	changeGender=()=> {//使用箭头函数,可先外出查找到this
		this.setState({gender: !this.state.gender})
	}
	render() {
		return (
			

你好!你的性别是{this.state.gender == 0 ? '女' : '男'} {this.state.genders}

) } }

props

props表示的是组件属性的实例对象

ReactDOM.render(,
 document.getElementById("test"))

例如以上代码,其props的值则为:

{
    "name": "xiaozhi",
    "age": "18"
}

我们可以在组件中,通过this.props来获取其属性和属性值

我们也可以通过批量进行传值

对props进行限制

在组件实例上有个属性propsType可以对其进行类型限制以及必要性约束(需要引props-type包)

props默认值

同理,在组件实例上有个属性defaultProps可以设置props的默认值

props约束简写

给类添加一个静态属性即可:static

class MyComponent extends React.Component {
	state = { gender: 0 }
	static propTypes = {
		name: PropTypes.string.isRequired,
		age: PropTypes.number,
		fn: PropTypes.func
	}
	static defaultProps = {
		sex: '男'
	}
	render() {
		let { name, sex } = this.props
		return (
			

{name}--{sex}

) } }

注意:props是不允许被修改的

关于constructor中props必传给super的问题

constructor(props){
	super(props)
    console.log(this.props)//如果不传,这里将会报undefined
}

官网说明

React.Component – React

在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。

通常,在 React 中,构造函数仅用于以下两种情况:

  • 通过给 this.state 赋值对象来初始化[内部 state。

  • 为事件处理函数]绑定实例

    constructor(props) {
    	super(props)
        //初始化[内部 state
    	this.state = {gender: 0}
        //事件处理函数]绑定实例
    	this.changeGender = this.changeGender.bind(this)
    }

constructor() 函数中不要调用 setState() 方法。如果你的组件需要使用内部 state,请直接在构造函数中为 this.state` 赋值初始 state

避免将 props 的值复制给 state!这是一个常见的错误.

refs

refs表示标签的唯一属性,用于获取元素节点实例,refs属性挂载在组件实例上。

string类型的refs

class MyComponent extends React.Component {
	getInput = () => {
		let { input1 } = this.refs
		console.log(input1.value)
	}
	getInput2 = () => {
		let { input2 } = this.refs
		console.log(input2.value)
	}
	render() {
		return (
			
) } }

注意:使用字符串形式的ref会有效率问题,不建议大量使用。

回调函数形式的refs

class MyComponent extends React.Component {
    getInput = () => {
        let { input1 } = this
        console.log(input1.value)
    }
    getInput2 = () => {
        let { input2 } = this
        console.log(input2.value)
    }
    render() {
        return (
            
{this.input1=a}} type="text" placeholder="输入" /> this.input2=c)} onBlur={this.getInput2} placeholder="点击输入" />
) } }

解析:

{this.input1=a}} type="text" placeholder="输入" />

ref={(a)=>{this.input1=a}}这段代码中,参数a表示的是节点的实例对象,this.input1=a表示的是将节点的实例对象挂载到组件实例上,名字取为input1。获取不在使用refs获取而是直接通过取的昵称获取到实例。

注意:关于调用次数问题,回调函数在初始化的时候会执行一次,当组件更新的时候,将执行两次。

如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

使用refs容器

react提供createRef来创建一个用于存储ref实例的一个容器,创建的容器只能存储一个ref实例,如果需要多个refshilling,则需要创建多个容器。使用current可以获取其实例

class MyComponent extends React.Component {
    inputRef = React.createRef();
    inputRef2 = React.createRef();
    getInput = () => {
        console.log(this.inputRef)
    }
    getInput2 = () => {
        let { input2 } = this
        console.log(this.inputRef2.current)
    }
    render() {
        return (
            
) } }

注意:低版本不支持该API

受控组件和非受控组件

受控组件:输入类型的标签,收集其值得时候,会随着值得变化而进行收集(类似vue的自动收集变量)

非受控组件:输入类型的标签,只有在使用数据的时候才会收集。

react事件处理

react的事件封装了原生的JavaScript事件(为了兼容),react事件使用了事件委托的方式绑定了事件,会将事件绑定到最外层的元素,可以通过target获取到操作的属性实例对象。

函数柯里化

class MyComponent extends React.Component {
    state = {
        name: '',
        pass: ''
    }
    submit = () => {
        console.log("@", this.state);
    }
    getData(types) {
        return (event) => {
            this.setState({ [types]: event.target.value })
            console.log(this.state);
        }
    }
    render() {
        return (
            


) } }

如以上代码,在无法实现同时接收多个参数的时候,可以使用高接函数-柯里化函数。

function fn(a){
    return(b)=>{
        return(c)=>{
            return a+b+c
            }
        }
}
console.log(fn(1)(2)(3));

这种方式就是函数柯里化

不使用函数柯里化自动收集变量

class MyComponent extends React.Component {
	state = {
		name: '',
		pass: ''
	}
	submit = () => {
		console.log("@", this.state);
	}
	getData(types,event) {
			this.setState({ [types]: event.target.value })
			console.log(this.state);	
	}
render() {
	return (
	
{this.getData('name', event)}} placeholder="用户名"/>
this.getData('pass', event)} placeholder="密码"/
) } }

这种方式就是在内联定义一个函数,在函数内调用组件内的方法。

react生命周期

react的生命周期主要有两个方面:组件挂载卸载,组件更新(父组件调render函数,调用serState函数,调用forceUpdate函数)。

组件挂载和卸载

constructor():构造器

componentWillMount():组件即将挂载

render():初始化调用一次,用于初始化数据

componentDidMount():组件已挂载

componentWillUnmount():组件即将卸载

componentDidUnmount():卸载组件(编程人员也可主动卸载)

执行顺序自上而下

注意:

render()函数在使用定时器的时候,不要进行状态修改,因为状态修改之后,会有调用render函数,这会导致进入递归死循环。

卸载组件的时候,一定要及时清理定时器,一般在componentWillUnmount()函数内清理

组件更新:

this.setState()

shouldComponentUpdate():控制组件是否进行更新,默认放回ture,放回false的时候,往后相关的生命周期函数将不执行

componentWillUpdate():组件即将更新

render():更新

componentDidUpdate():组件更新完毕

this.forceUpdata()

表示强制更新,一般使用场景是在不更新状态的情况下,进行页面更新。

componentWillUpdate():组件即将更新

render():更新

componentDidUpdate():组件更新完毕

父组件调用render()

  • componentWillReceiveProps:表示接收父组件传入的props属性时调用,参数为props,注意:第一次转入不算,只有从第二次props发生改变时才会调用。

  • shouldComponentUpdate

  • componentWillUpdate

  • render

  • componentDidUpdate

这个是v16.3版本以前的声明周期函数,v16.3对react的声明周期函数进行了更新和弃用,并在v17版本正式启用。

17版本新的声明周期函数

componentWillMount,componentWillUpdate,componentWillReceiveProps三个生命周期函数改名了,如果需要使用这三个声明周期,需要加前缀UNSAFE_

原因官网给出的介绍为:

这些生命周期方法经常被误解和滥用;此外,我们预计,在异步渲染中,它们潜在的误用问题可能更大。我们将在即将发布的版本中为这些生命周期添加 “UNSAFE_” 前缀。(这里的 “unsafe” 不是指安全性,而是表示使用这些生命周期的代码在 React 的未来版本中更有可能出现 bug,尤其是在启用异步渲染之后。)

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

static getDerivedStateFromProps(props, state){
    return {
        name: props.name,
        age:props.age
    }
}

此方法适用于罕见的用例,即 state 的值在任何时候都取决于 props。

派生状态会导致代码冗余,并使组件难以维护。即没啥大的意义!

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()

此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。

应返回 snapshot 的值(任意非null值都是快照值)(或 null)。

常用的三个声明周期钩子函数:

render,componentDidUpdate,componentWillUnmount。

react脚手架

全局安装脚手架

npm install -g create-react-app

创建脚手架

npx create-react-app my-app cd my-app npm start

如果发现无法安装,是因为以前可能全局安装了低版本的create-react-app,卸载掉即可

或者忽略全局的脚手架:执行

npx --ignore-existing create-react-app my-app

还有问题,请移步该博客

安装create-react-app失败遇到的问题_宽粉儿~的博客-CSDN博客_react安装失败

使用脚手架创建第一个react组件

创建一个文件夹component用于存放组件,style文件夹用于存放样式。

在MyComponent.jsx文件写代码

import React from 'react'
//import '../style/index.css' 这种方式也可以
import style from '../style/index.css' //样式模块化,用的不多,一般都是用上面那种
class MyComponent extends React.Component { 
    render() { 
        return (
            

这是脚手架第一个组价

) } } export { MyComponent}

index.css样式代码

.title{
    color: red;
}

在App.js引入组件并渲染

import {MyComponent} from './component/MyComponent'
function App() {
  return (
    
); } export default App;

组件间通信

父子组件通信

使用props进行通信,请移步至props知识点

兄弟组件通信

将需要操作的数据存放在父组件上,通过在父组件定义方法间接传递数据

父组件

class App extends React.Component {
  state = {
    itemList: ["吃饭", "喝酒", "看电视"]
  }
  addItem = (item) => {
    let newState = this.state.itemList.push(item)
    this.setState({
      itemList: newState
    })
  }
  render() {
    return (
      
      {/* 先子组件传方法间接修改数据 */}                 {/* 兄弟组件二:用于展示数据 */}              
  ) } }

Add组件

class Add extends React.Component {
    add = (event) => {
        if (event.keyCode === 13) {
        this.props.addItem(event.target.value)
        }
    }
    render() { 
        return (
            
this.add(event)}/>
) } }

通过执行父组件的方法,间接父组件的数据,从而实现兄弟组件之间的数据通信。

消息订阅与发布

使用pubsub插件库

npm i pubsub-js

发布消息:用于传递数据

PubSub.publish('MY TOPIC', 'hello world!');

订阅消息:用于接收数据

var mySubscriber = function (msg, data) {
    console.log( msg, data );
};
var token = PubSub.subscribe('MY TOPIC', mySubscriber);

取消订阅发布

PubSub.unsubscribe(token);

子父之间的数据通信

方式和兄弟之间通信类似,在父组件上定义一个 修改父组件数据的方法,传给子组件,子组件调用该方法,传入相关的参数即可实现子父之间的数据传输。

网络请求

使用axios发送请求并封装axios

博主使用的技术栈是vue,之前自己封装过,就不浪费时间进行封装了,请移步axios官网:

axios中文网|axios API 中文文档 | axios

import axios from 'axios'

componentDidMount() { 
        axios.get('https://mock.apipost.cn/app/mock/project/fee3d514-9c37-4f69-9be0-ea185d142d90/getname').then(res => { 
            console.log(res);
        }).catch(error => { 
            console.log(error);
        })
    }

这是我用apiPost写的一个mock接口,模拟请求,想要实现可以到官网上下载apiPost

fetch

fetch是一个内置的请求,只要promise过关,不难,所以就不对说了

Fetch API - Web API 接口参考 | MDN

react配置代理

方式一: 在package.json中配置“proxy”选项

{
  "proxy":"http://localhost:3000"
}

这种方式只能配置一个代理

方式二:新建setupProxy.js文件(命名必须是这个)

const proxy = require('http-proxy-middleware')
module.exports = function (app) {
  app.use(
    proxy('/api', {
      target: 'http://localhost:3000',
      changeOrigin: true,
      pathRewrite: {
        '^/api': '',
      },
    })
  )
}

该方式可以配置多个代理。

react-router-dom

该库是专门为web设计的路由库,使用的版本是V5版本,和V6版本有所不同。

组件

:使用history模式,刷新页面会请求相关资源,会导致props丢失问题。
:使用hash模式,刷新页面不会发请求
:路由入口
组件的升级版,自带高亮效果,可设置activeClassName属性指定样式类名实现高亮效果

:重定向
:路由出口


:注册路由(V5),用于路由一一匹配
:注册路由(V6),替换用于,这是新版本的内容}>

组件封装:以封装NavLink为例

定义组件:MyComponent.jsx

import {Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class List extends Component {
    render() { 
        console.log(this.props);
        return (
            
{ this.props.children}
) } }

使用组件

import MyComponent from './component/MyComponent'
关于

注意:可以使用this.props.children获取组件的标签体。

Switch组件的使用



老版本这样是,进入/B路由都会渲染B和A路由组件,但是路由是需要一一对应的,所以可以使用 Switch对其进行注册,实现一一对应。

        
          
          
          
        

使用Switch就可保证在进入/B路由的时候,只会渲染B组件。

关于使用BrowserRouter方式导致请求出错的问题

在index.html中引入样式

在组件中,使用相关样式规则

进入A 
 进入B

当页面启动时,请求的样式资源路径为:

http://localhost:3000/style.css

此时,我们在路径新的访问接口为

进入A 
 进入B

第一次请求的资源路径为:

http://localhost:3000/style.css

刷新页面后的请求资源路径为:

http://localhost:3000/A/style.css

这就导致样式未加载到,

解决方式:

引入资源使用相对路径


 改为

 或者
 
 或者

路由匹配

进入A 

react路由匹配默认开启模糊匹配,在进入路径/A/B/C一般会匹配路由路径/A/B/C,如果没有将会匹配到含有该路径的路由,即匹配到/A

开启精准匹配

精准匹配尽量别开启,因为在二级路由的时候,容易导致匹配不到。

Redirect


  
  
  

表示匹配不到时将会进B这个组件

路由嵌套

只需在该路由下再写入一个路由即可

如,我们为A路由嵌套一个路由

render() {
    return (
      
       进入A         进入B                                    
  ) }

在A路由组件写入代码:

render() { 
        return (
            
点我
) }

这样即可实现路由的嵌套。

路由传参

params参数

class A extends React.Component {
    state = {msg: [{id: 1,name:'小智'},{id: 2,name:'小红'}]}
render() { 
    let { msg}=this.state
    return (
        
{                msg.map((item, index) => { return ({ item.name})   }) }
)}}

在入口Link将参数拼接到路由地址上,在出口Route使用占位符来表示参数,在该路由路径所对应的组件的props属性就可以获取到路由参数

return (
    
{this.props.match.params.id}+{ this.props.match.params.name}
)

search参数

该方式就是向路由地址拼接参数

return (
    
{ msg.map((item, index) => { return ( { item.name}) })}
)

在props的localtoin有个属性叫search的就携带了该参数

search: "?id=1&name=小智"

我们先需要将?截取掉在将其转为对象,react提供了一个库叫qs(有的版本叫querystring),可以将其转成对象

import qs from 'qs'
    class C extends React.Component {   
        render() { 
            let querys = qs.parse(this.props.location.search.slice(1))
    return (
        
{querys.id}+{ querys.name}
) } }

state参数

不同于组件的state属性。

class A extends React.Component {
    state = {
        msg: [{id: 1,name:'小智'},{id: 2,name:'小红'}]
    }
render() { 
    let { msg}=this.state
    return (
        
{ msg.map((item, index) => { return ( { pathname: '/A/C', state: {id:item.id,name:item.name}}}>{ item.name} ) }) }
) } }

传参形式

{ pathname: '/A/C', state: {id:item.id,name:item.name}}}>{ item.name}

在相对应的路由

class C extends React.Component {   
    render() { 
        let querys = this.props.location.state
        console.log(this.props.location);
    return (
        
{querys.id}+{ querys.name}
)}}

这个方式参数不会在地址栏显示,刷新参数也不会丢失。

三种形式使用频率自上而下变少

参数模式

一共有两种模式,push和replace,默认push模式,即有浏览记录,replace表示的是通过路由地址替换,无法实现路由的前进和后退功能。

开启replace模式

{ pathname: '/A/C', state: {id:item.id,name:item.name}}}>

编程式路由导航

this.props.history提供了两种路由跳转方式:push(path,state)和replace(path,state)

class C extends React.Component {
    state = {
        person: {name: 'xiaozhi',age:18}
    }
    gotoD = (p) => { this.props.history.push("/D",p)}
render() { 
    return (
        
this.gotoD(this.state.person)}/>
) } }

这种方式传的参数是state,其他方式只有一个参数,即参数拼接在url上。注意,路由D一定要注册,我这注册的地址为:

在App.jsx文件注册的。

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章