React组件首字母需要大写
useState和useRef用哪个
useState会触发页面更新,useRef不会
Partial
// Partial可以将所有属性变成可选的
export type UserinfoPatchBody = Partial<UserInfo>
css属性中的user-select
user-select 属性规定是否能选取元素的文本。
在 web 浏览器中,如果您在文本上双击,文本会被选取或高亮显示。此属性用于阻止这种行为。
sass
定义变量 要用$开头
@mixin与@include配合使用
column-gap: 40px;
指定列之间的的间隙为 40 个像素:
SEO优化
<img src={logo} alt="测评系统" />
<h1 style={{ fontSize: 0 }}>测评系统</h1>
图片地址以//开头
1、它会判断当前的页面协议是http 还是 https 来决定请求 url 的协议。
2、用于处理 网站使用的协议和 网页中请求的外网资源不一致的问题。
3、这种写法,也使用于CSS
React.Fragment
React.Fragment组件能够在不额外创建 DOM 元素的情况下,让 render()方法中返回多个元素。相当于空标签<></>。
alias @设置为src路径
在ts.config.json中再配置
图片不显示问题
如果没有将图片放入public文件夹下,在其他文件夹下通过img标签使用会出现图片不显示问题,
这个时候使用import导入的方式即可使用
import logo from './assets/logo.png'
路由
可以把路由信息放入config.ts中,在App.tsx中导入使用
type RouterDataType=typeof RouterData
export type RouterKeys=keyof RouterDataType
// 路由信息
export const RouterData={
// 单词应该为correct
corret_exam_list:{
path:'/corret_exam_list'
},
subject_add:{
path:'/subject_add'
},
subject_manage:{
path:'/subject_manage'
},
student_manage:{
path:'/student_manage'
}
}
设置监听当前路由变化的hooks
import { useLocation } from 'react-router-dom';
// 监视当前路由的变化
export function usePathKey(){
let location=useLocation()
let pathKey=location.pathname
return pathKey
}
倒计时
通过useEffect进行对count监听
// 设置倒计时
const [count, setCount] = useState(0)
// 验证码倒计时
let TIME = 60
// 倒计时
useEffect(() => {
if (count === 0) return
setTimeout(() => {
setCount(count - 1)
}, 1000)
}, [count])
// 获取验证码
const startCode = () => {
form
.validateFields(['username'])
.then(() => {
setCount(TIME)
})
.catch(() => {})
}
阻止冒泡
<span className={styles.delete} onClick={e => deleteSubjectTwo(e, item)}>
删除
</span>
// 删除题目
const deleteSubjectTwo = async (e: any, item: any) => {
e.stopPropagation()
const res = await request.delete(`/api/topic/${item._id}`)
if (res.status === 200) {
message.success('删除成功')
}
dispatch(set_active_topic(null))
dispatch(get_topic_list_async(item.two_id))
}
给每个孩子循环添加key
// 循环添加key
function addSubjectTreeKey(subject_tree: any) {
const _tree = JSON.parse(JSON.stringify(subject_tree))
const data = _tree.map((item: any) => {
let children
item.key = item.value
if (item.children?.length > 0) {
children = addSubjectTreeKey(item.children)
}
return {
...item,
children
}
})
return data
}
对props定义类型
父组件
<AddSubjectTwo showing={isModalOpen} changeShowing={changeModal}></AddSubjectTwo>
子组件
type AddSubjectTwoType = {
showing: boolean
changeShowing: (a: boolean) => void
}
// 内嵌表格
export default function AddSubjectTwo({ showing, changeShowing }: AddSubjectTwoType) {}
变量后使用 !
变量后使用 !:表示类型推断排除null、undefined
// 变量后使用 !:表示类型推断排除null、undefined
const fileName = avatar.split('/').at(-1)!
No routes matched location
错误写法
// 阅卷
corret_exam: {
path: '/corret_exam',
hasMenu: false
}
正确写法
// 阅卷
corret_exam: {
path: '/corret_exam/:exam_id',
hasMenu: false
}
可以将参数放入路径的后面作为传参的方式,这样避免刷新导致参数消失,放入redux中就会产生这种问题
// 前往阅卷
function goCorretExam(record: any) {
// 采用此方式刷新后就会导致数据消失
// dispatch(set_current_exam_topic_id(record._id))
// 通过 路由传过去可以防止刷新数据消失
navigate(`/corret_exam/${record._id}`)
}
useEffect
有些解决不了的问题可以通过useEffect巧妙的解决,只要在[]后填上合理的参数
classNames
下载classnames的包
然后使用
<div className={`${styles.wrap} ${classNames({ read: type === 'read' })}`}>
事件总线处理状态码等问题
下载events包
在util文件夹下创建event.js文件
import { EventEmitter } from "events";
// 全局发布订阅
const eventBus = new EventEmitter()
export default eventBus
在响应拦截器中进行使用
// 响应拦截器
instance.interceptors.response.use(
function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
console.log('响应状态码', response.status)
if (response.status === 200) {
// 401未登录
if (response.data.code === 401) {
eventBus.emit('global_not_login', response.data.msg)
}
// 业务错误
if (response.data.code === -1) {
eventBus.emit('global_error_tips', response.data.msg)
}
} else if (response.status === 403) {
eventBus.emit('global_error_auth', '没有权限,爬一边去')
}
return response
},
function (err) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
// console.log('响应拦截器中的失败响应', err.response.status)
eventBus.emit('global_error_tips', err.response.data.msg)
return Promise.reject(err)
}
)
然后在App.tsx中接收
// 处理错误的函数
const openNotification = (msg: string) => {
notification.error({
message: '错误',
description: `错误信息:${msg}`
})
}
function App() {
const dispatch = useAppDispatch()
const navigate = useNavigate()
// 获取用户信息
useEffect(() => {
dispatch(get_user_info_async())
// 未登录
eventBus.on('global_not_login', function (msg) {
navigate('/login')
openNotification('哥你至少得登录吧')
})
// 业务错误
eventBus.on('global_error_tips', function (msg) {
openNotification(msg)
})
// 没有权限
eventBus.on('global_error_auth', function (msg) {
openNotification(msg)
LogoutPost().then(() => {
navigate('/login')
})
})
}, [])
...
懒加载
// 懒加载
const AsyncSubjectAdd = lazy(() => import('./pages/subject_add'))
const AsyncSubjectManage = lazy(() => import('./pages/subject_manage'))
// Suspense与lazy配合shiyong
function SubjectManage() {
return (
<Suspense fallback={'loading'}>
<AsyncSubjectManage></AsyncSubjectManage>
</Suspense>
)
}
function SubjectAdd() {
return (
<Suspense fallback={'loading'}>
<AsyncSubjectAdd></AsyncSubjectAdd>
</Suspense>
)
}
统计渲染次数的hooks
import { useRef } from 'react'
// 渲染次数统计
export default function useRenderCount(name: string) {
const ref = useRef(0)
ref.current++
console.log(`${name}已经渲染了${ref.current}次`)
}
进入新页面后仍存在上个页面的数据
useEffect的中return函数为componentWillUnmount生命周期
下载js-cookie
js-cookie是获取Cookie的值
ErrorBoundary
import React, { Component } from 'react'
// 组件渲染错误处理,可以放入全局中比如index.tsx 或者单独放入复杂或者不稳定的组件
export default class ErrorBoundary extends Component {
state: any
props: any
constructor(props: any) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: any) {
return { hasError: true }
}
render() {
if (this.state.hasError) {
return <h1>鸽鸽,页面出错啦</h1>
}
return this.props.children
}
}