React最常见的使用小tip

有状态是复杂组件
无状态是简单组件

state简写方式

class Demo extends React.Component{
    // 初始化状态
    state = {isState:false}
    render(){
        const {isState} = this.state
        return(<div>state简写{isState}</div>)
    }
}

ref的写法方式

<input ref={(current)=>{this.refDom = current}}/> // 不推荐 但也无关紧要
<input ref={this.saveRefInput}/> // 推荐
saveRefInput = (c) => {
    this.input = c
    console.log(c)
}
// 最新 createRef 形式
myRef = React.createRef // 该容器是一对一的形式
<input ref={this.myRef}/> 
console.log(this.myRef.current)

定时器需要在页面销毁时关闭

getDerivedStateFormProps 介于构造器和render之间

static getDerivedStateFormProps(props,state){return null}
// state的值在任何时候都取决于props

getSnapshotBeforeUpdate 介于render和update之间

getSnapshotBeforeUpdate(){return '任何返回值将作为参数传递给componedtDidUpdate'}
// 提交到DOM节点之前调用,更新之前获取一些信息(例如滚动位置)

遍历列表是最好不要用index作为key 会影响性能 使用每条数据对唯一标识作为key
使用index作为key可能会引发对问题:

  1. 若对数据进行 逆序添加,删除等破坏顺序操作 会产生没有必要的真实DOM更新==》界面效果没问题,但效率低
  2. 如果结构中还包含输入类的DOM 会产生错误DOM更新==》界面有问题
  3. 如果不存在对数据的逆序添加,删除等破坏操作,仅用于渲染展示,使用index作为key是没有问题的

react脚手架

  1. 全局安装 npm install -g create-react-app
  2. 切换到想创建项目的目录 再使用命令 create-react-app demo-react
  3. 进入项目 cd demo-react
  4. 启动项目 npm start

public文件夹里的文件:

  1. noscript 浏览器不支持js则展示标签中的内容
  2. robots.txt 爬虫规则文件

scr里的文件:

<React.StrictMode>
<App/>
</React.StrictMode>
// 可以检查代码里的小瑕疵 让代码写的更加规范

reportWebVitasl.js页面性能检测
setupTest.js 用于组件测试

组件的文件后缀可以使用 .jsx

项目文件里的引用顺序:

  1. 第三方包放最上面
  2. 自己写的的包放下面
  3. style文件放最下面

往数组里添加数据

const {todos} = this.state;
const todoObj = {}
const newTodos = [todoObj,...todos]
this.setState({
 todos:newTodos
})

遍历数组更改相符合的一项返回,例如接收需要更改的id和要更改的状态

update = (id,done){
    const {todos} = this.state;
    const newTodos = todos.map(todoObj=>{
        if(todoObj.id === id) return {...todoObj,done}
        else return todoObj
    })
    // 更新数据状态
    this.setState({
        todos:newTodos
    })
}

删除一个对象 用filter处理

delete = (id) => {
    const {todos} = this.state;
    const newTodos = todos.filter(todoObj=>{
        return todoObj.id !== id
    })
    // 更新数据状态
    this.setState({
        todos:newTodos
    })
}

数组 reduce 统计求和

// todos里的done如果完成了就+1 否则就+0
const doneCount = todos.reduce((pre,cur)=>{return pre + (todo.done ? 1 : 0)},0)

state在哪里 操作方法就在哪里

  1. react本身只关注界面,不包含发送ajax请求的代码
  2. 前端应用需要通过ajax请求与后台进行交互
  3. react应用轴需要集成第三方ajax库或自己封装

react脚手架配置代理
单个代理 package.json 添加 ‘proxy’:'127.0.0.1:8000’
多个代理 新增文件 setupProxy.js 写如下代码

const proxy = require('http-proxy-middleware')
module.exports = function(app){
    app.use(
        proxy('/api1',{ // 遇见/api1前缀的请求,就会触发该代理配置
            target:'http://localhost:5000', // 请求转发给谁
            changeOrigin:true, // 控制服务器收到的请求头中host字段的值
            pathRewrite:{'^/api1':''} // 重写请求路径(必须)
        }),
        proxy('/api2',{
            target:'http://localhost:5001',
            changeOrigin:true,
            pathRewrite:{'^/api2':''}
        })
    )
}

连续结构赋值

const {a:{b:{c}}} = obj
console.log(c)

连续解构赋值加重命名

const {a:b:data} = obj
console.log(data

)

三元表达式连写

isFirst ? <h1>1</h1> : isLoad ? <h2>2</h2> : err ? <h3>3</h3> : <h4>done</h4>

组件传值
父传子 子传父 props
兄弟组件传递数据
订阅消息:
1. 消息名
2. 发布消息
PubSubJS

npm install pubsub-js

兄弟1 发布消息

import PubSub from 'pubsub-js'
onSedData = () => {
    const obj = {name:'xxx',age:18}
    // 在任何非父子组件传递数据时都可使用
    PubSub.publish('test',{obj})
}

兄弟2 订阅消息

import PubSub from 'pubsub-js'
componentDidMount(){
    // 订阅消息
    this.token = PubSub.subscribe('test',(_,data)=>{
        console.log(data) // 收到数据
    })
}
// 取消订阅
componentWillUnmount(){
    PubSub.unsubscribe(this.token)
}

fetch请求 浏览器自带请求 不需要下第三方库

react路由
前端路由就是history

react-router-dom

npm install react-router-com

react靠路由链接切换组件

路由 # 后面属于 hash值(不会发给服务器) HashRoute
BrowsesRouter 不带 # 号 一般使用这种

路由组件和一般组件

  1. 写法不同
    一般组件 <Demo/>
    路由组件 <Route path="/demo" component={Demo}/>
  2. 存放位置
    一般组件 components 放在项目文件里
    路由组件 pages 放在项目文件里
  3. 接收到得props不同
    一般组件 组件里传递了什么就能接收到什么
    路由组件 接收到三个固定得属性 history location match

NavLink能自动为当前点击得标签添加 active class类名并且通过activeClassName修改

封装 NavLink

import React,{Component} from 'react'
import {NavLink} from 'react-router-dom'
export default class MyNavLink extends Component{
		render(){
		  return(<NavLink activeClassName="newClass" className="listItem" {...this.props}/>)
		}
}

使用

<MyNavLink to="/home" a={1} b={2}>Home</MyNavLink>

Switch的使用 阻止路由匹配到了相对应的路由不再往下匹配
引入 Switch 组件 使用 Switch 组件把路由包裹

  1. 通常情况下 path 和 component 是一一对应关系
  2. Switch 可以提高路由匹配效率(单一匹配)

解决多级路径刷新页面样式丢失的问题

  1. public/index/html 中 引入样式时不写 .// (常用)
  2. public/index/html 中 引入样式不写 ./%PUBLIC_URL% (常用)
  3. 使用 `HashR路由

严格匹配与模糊匹配

  1. 默认使用的是模糊匹配 必须包含要匹配的路径 且顺序要一致
  2. 开启严格匹配 添加属性 exact
  3. 严格匹配不要随便开启,需要时再开,有些时候会导致无法继续匹配二级路由

嵌套路由

  1. 注册子路由时要写上父路由的path值
  2. 路由的匹配是按照注册路由的顺序进行的

路由组件传递参数

  1. params参数
路由链接(携带参数):<Link to="/demo/tom/18">params参数</Link>
注册路由(声明接收):<Route path="/demo/:name/:age" component={Demo}/>
接收参数: const {name,age} = this.props.match.params
  1. search参数
引入库 import qs from 'querystring'
路由链接(携带参数):<Link to="/demo/?name=tom&age=18">search参数</Link>
注册路由(无需声明正常注册即可):<Route path="/demo" component={Demo}/>
接收参数: this.props.location.search
备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
  1. state参数
路由链接(携带参数):<Link to={{path:'/demo',state:{name:'tom',age:18}}}>state参数</Link>
注册路由(无需声明正常注册即可):<Route path="/demo" component={Demo}/>
接收参数: const{name,age} = this.props.location.state || {}
备注: 刷新页可以保留住参数,参数不在地址栏上体现

编程式导航 借助this.props.history对象上的api进行操作

withRouter:

  1. 可以加工一般组件 可以让一般组件具备路由组件所特有的api
  2. withRouter返回值是一个新组件

BrowerRouter 与 HashRouter 的区别

  1. 底层原理不一样
    BrowerRouter 使用的是 H5 的history api,不兼容ie9及以下的版本
    HashRouter 使用的是url的哈希值
  2. path表现形式不一样
    BrowerRouter 的路径中没有# 例如:localhost:3000/demo
    HashRouter 的路径包含# 例如:localhost:3000/#/demo
  3. 刷新后对路由state参数的影响
    BrowerRouter 没有任何影响,因为state保存在history对象中
    HashRouter 会导致路由state参数的丢失
  4. 备注:HashRouter可以用于解决一些路径错误相关的问题

setState更新状态的两种写法

// 对象式的setState
this.setState({count:count+1},()=>{console.log(this.state.count)}) 
// 函数式的setState
this.setState((state,props)=>{return {count:state.count+1}})

lazyLoad

import React,{Component,lazy,Suspense} from 'react;
const Home = lazy(()=>import('./Home'))
<Suspense fallback={<h1>loading......</h1>}>
    <Route path="/home" component={Home}/>
</Suspense>

Hooks

  1. 是react16.8版本新增加的新特性/新语法
  2. 可以在函数中使用state以及其他的react特性

三个常用的Hook
State Hook: React.useState()

  1. 让函数组件也可以有state状态,并进行状态数据的读写操作
  2. 语法:
    const [xxx,setXxx] = React.useState(initValue)
  3. userState()说明
    参数:第一次初始化指定的值在内部操作
    返回值:包含2个元素的数组,第一个为内部当前状态值,第二个为更新状态值的函数
  4. setXxx() 两种写法
    setXxx(newValue): 参数为非函数值,直接指定新的状态值,内部用覆盖原来状态值
    setXxx(value=>newValue): 参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值

Effect Hook: React.useEffect()

  1. 让函数组件执行副作用操作(用于模拟类组件中的生命周期钩子)
  2. react中的副作用操作:
    发ajax请求数据获取
    设置订阅/启动定时器
    手动更改真实DOM
  3. 语法和说明:
    useEffect(()=>{
    // 在此可以执行任何带副作用操作
    return()=>{
    // 组件卸载前执行
    // 在此做一些收尾工作,比如清除定时器/取消订阅等
    }
    },[stateValue]) // 如果指定的是[],回调函数只会在第一次render()后执行
  4. 可以把 useEffect Hook看作如下三个函数的组合
    componentDidMount()
    componentDidUpdate()
    componentWillUnmount()

Ref Hook: React.useRef()

  1. Ref Hook 可以在函数组件中存储。查找组件内部标签或任意其它数据
  2. 语法: const refContainer = useRef()
  3. 作用:保存标签对象,功能与React.createRef()一样

Fragment 文档碎片
等同于 <></>
区别 Fragment可以指定key 但是空标签不可以
不可以传值

Context 一种组件间通信方式 常用于 祖组件 与 后代组件 间通信
使用

// 创建Context容器对象
const XxxContext = React.createContext()
// 渲染子组件时,外面包裹XxxContext.Provider 通过value属性给后代组件传递数据
<XxxContext.Provider value={数据}></XxxContext.Provider>
// 后代组件读取数据
// 第一种方式 仅适用于类组件
static contextType = XxxContext // 声明接收context
this.context // 读取context中的value数据
// 第二种方式 函数组件与类组件都可以 value就是context中的value数据
<XxxContext.Consumer>
	{value=>(要显示的内容)
</XxxContext.Consumer>

注意:在应用开发中一般不用context,一般都用它的封装react插件

Component的问题

  1. 只要执行setState即使不改变状态数据,组件也会重新render
  2. 只当前组件重新render,就会自动重新render子组件==>效率低

原因:
Component中的shouldComponentUpdate总是返回true

解决:

  1. 重写shouldComponentUpdate方法,比较旧的state或props,如果有变化才返回true,否则false
  2. 使用 PureComponent(重写了shouldComponentUpdate)

注意:
只是进行state和props数据的浅比较,如果只是数据对象内部数据变了,返回false
不要直接修改state数据,而是要产生新数据

项目中一般使用PureComponent来优化

render props
如何向组件内部动态传入带内容的结构(标签)
vue中:
使用slot技术
react
使用children props: 通过组件标签传入标签
使用render props: 通过组件标签属性传入结构,一般用render函数属性

// children props
<A>
  <B>xxxxx</B>
</A>
{this.props.children}
// 问题:如果B组件需要A组件内的数据,==>做不到
// render props
<A render={(data)=><C data={data}></C>}></A>
// A组件:{this.props.render(内部state数据)}
// C组件:读取A组件传入的数据显示:{this.props.data}

错误边界 Error boundary
特点:只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件合成事件,定时器中产生的错误

用于生产环境,开发环境如果有错误还是会出现错误

state = {
    hasError:'', // 用于标示子组件是否产生错误
}
// 当parent的子组件出现报错的时候,就会触发,并携带错误信息
static getDerivedStateFromError(error){
    return {hasError:error}
}
componentDidCatch(){
    console.log('此处统计错误,反馈给后台')
}
render(){
    return(<div>{this.state.hasError ? <p>当前网络不稳定请稍后再试</p> : <Child/>}</div>)
}

组件通信方式
组件间的关系:
父子组件
兄弟组件(非嵌套组件)
祖孙组件(跨级组件)

几种通信方式:
1.props
children props
render props
2. 订阅发布
pubs-sub event等等
3. 集中式管理
redux dva等等
4. conText
生产者–消费者模式

比较好的搭配方式
父子组件: props
兄弟组件: 消息订阅-发布,集中式管理
祖孙组件: 消息订阅-发布,集中式管理,conText(开发用得少,封装插件用得多)

通过 serve 可以运行前端打包文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值