TS与React结合小细节
零碎
创建项目:
create-react-app 名字 --template typescript
router中需要创建index.tsx
用ts的话,既然写了tsx,就需要导入react:
import React from 'react'
组件导出首字母必须要大写
const [ banner, setBanner ] = useState<any []>([])
【设置类型】定义了promise,需要传入参数,是为了显示resolve的类型
const a = new Promise<string>((resolev, reject)=>{
// 限制它的类型
resolve("aaa")
}).then(res =>{
console.log(res)
})
初始化构建渲染页面的时候
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(<App></App>)
ts+react导入图片模块
// 在src加创建:react-app-env.d.ts
// 加入:
/// <reference types="react-scripts" />
配置路径:
// 在tsconfig.json加入:
{
"compilerOptions": {
...
// 加入baseUrl和paths
"baseUrl": ".",
"paths": {
"@/*":[
"src/*"
]
}
},
"include": [
"src"
]
}
函数式组件参数类型约束
// 第一中写法
import React, { memo } from 'react'
interface IPerson{
name: string,
age: number,
height?: string
}
const download = memo((props: IPerson) => {
return (
<div>download</div>
)
})
export default download
// 第二种写法:推荐
import React, { memo, FC } from 'react'
interface IPerson{
name: string,
age: number,
height?: string
}
// 使用泛型将类型设置给props
const Download: FC<IPerson> = memo((props) => {
return (
<div>
<div>{ props.name }</div>
<div>{ props.age }</div>
<div>{ props.height }</div>
</div>
)
})
export default download
// 当我们设置React.FC<IPerson>后,然后我们想要实现类似于插槽的作用,需要在IPerson中设置children
import React, { memo } from 'react'
import type { ReactNode, FC } from 'react'
interface IPerson{
name: string,
age: number,
height?: string,
children?: ReactNode
}
const Download: FC<IPerson> = memo((props) => {
// 这里我们可以设置默认值
const { name = 'hh', ... } = props
return (
<div>
<div>{ props.name }</div>
<div>{ props.age }</div>
<div>{ props.height }</div>
<div>{ props.children }</div>
</div>
)
})
export default Download
类组件参数类型约束
props约束
import React, { PureComponent } from 'react'
interface IPerson{
name: string,
age?: number
}
// 接收类型在PureComponent的泛型写法中
export class index extends PureComponent<IPerson> {
constructor(props: IPerson){
super(props)
}
render() {
return (
<div>index</div>
)
}
}
export default index
state类型
import React, { PureComponent } from 'react'
interface IProps{
name: string,
age?: number
}
interface IState{
aa: string,
bb: number
}
// 接收第二个参数
export class index extends PureComponent<IProps, IState> {
constructor(props: IProps){
super(props)
this.state = {
aa:'aaa',
bb:11,
}
}
render() {
return (
<div>index</div>
)
}
}
export default index
//简便写法:成员变量可以在constructor外定义,同时constructor默认就会进行super操作,可以隐藏
import React, { PureComponent } from 'react'
interface IProps{
name: string,
age?: number
}
interface IState{
aa: string,
bb: number
}
export class index extends PureComponent<IProps, IState> {
state: IState = {
aa:'aa',
bb:11
}
// constructor(props: IProps){
// super(props)
// this.state = {
// aa:'aaa',
// bb:11,
// }
// }
render() {
return (
<div>index</div>
)
}
}
export default index
redux的类型封装
最主要是要封装useSelector,useDispatch和shallowEqual是为了统一在一个文件中加进来的
// store/modules/count.ts
import { createSlice } from '@reduxjs/toolkit'
const countSlice = createSlice({
name:"count",
initialState:{
num:10
},
reducers:{
addNum(state, action){
state.num = action.payload
}
}
})
export const { addNum } = countSlice.actions
export default countSlice.reducer
// store/index.ts
import { configureStore } from "@reduxjs/toolkit"
import countReducer from './modules/count'
import { useSelector, useDispatch, shallowEqual } from 'react-redux'
import type { TypedUseSelectorHook } from 'react-redux'
const store = configureStore({
reducer: {
count: countReducer,
}
})
// 好好理解理解
// 将 useSelector 与ts进行一个结合封装一下 到时候我们直接使用 useAppSelector 就会类型推断自动判断我们的类型
// 拿useSelector的类型
type StateFnType = typeof store.getState
type RootState = ReturnType<StateFnType>
// 拿useDispatch的类型
type DispatchType = typeof store.dispatch
export const useAppSelector: TypedUseSelectorHook<RootState>= useSelector
export const useAppDispatch: ()=> DispatchType = useDispatch
// 这部其实意义不大,但是为了统一一下,我们就进行一个赋值,然后都在这个文件导出
export const useAppShallowEqual = shallowEqual
export default store
// 项目文件中
import React, { memo } from 'react'
// 自己封装的类型
import { useAppSelector, useAppDispatch, useAppShallowEqual } from '@/store'
import { addNum } from './store/modules/count'
const App = memo(() => {
const { count } = useAppSelector((state) =>({
count: state.count.num,
}), useAppShallowEqual)
const dispatch = useAppDispatch()
const change = () =>{
dispatch(addNum(20))
}
return (
<div>
{ count }
<button onClick={change}>11</button>
</div>
)
})
export default App
redux类型推导补充
当我们在定义initialState的时候,一般我们是不需要进行类型声明的,因为他会自动进行类型推导,但是如果我们定义的变量不容易进行类型推导的话(比如我们定义了常量联合类型、数组等),我们也可以对其进行一个类型声明
import { createSlice } from '@reduxjs/toolkit'
interface ISate{
num: num,
info: string
position: 'left' | 'right'
}
const initialState: IState = {
num:10,
inof: '信息',
position: 'right'
}
const countSlice = createSlice({
name:"count",
initialState,
reducers:{
addNum(state, action){
state.num = action.payload
}
}
})
export const { addNum } = countSlice.actions
export default countSlice.reducer
同时我们也可以对action.payload定义类型
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
const countSlice = createSlice({
name:"count",
initialState:{
num:10
},
reducers:{
// 接收泛型,当然结构的写法也是可以这样的 { payload }: PayloadAction<number>
addNum(state, action: PayloadAction<number>){
state.num = action.payload
}
}
})
export const { addNum } = countSlice.actions
export default countSlice.reducer
redux异步操作的类型
createAsyncThunk也可以传入类型
// jsx
createAsyncThunk("fetch", async (payload, {dispatch, getState })=>{...})
// tsx:三个类型:返回值类型 + payload类型 + state类型(一般是我们在总模块中定义的类型)
createAsyncThunk<void, number, { state: RootState }>("fetch", async (payload, {dispatch, getState })=>{...})
useRef类型指定
当我们将useRef绑定给一个组件的时候,必须要设置类型,因为我们拿current的时候需要有自己的类型
// 我们以antd中的Carousel为例,我们需要拿到Carousel
import React, { memo, useRef } from 'react'
import type { FC, ReactNode, ElementRef } from 'react'
import { Carousel } from 'antd';
interface IPerson{
children?: ReactNode
}
const Banner: FC<IPerson> = memo(() => {
// 获取Carousel的类型,使用ElementRef,并且不能初始化为空,所有我们设置初始化为null
const bannerRel = useRef<ElementRef<typeof Carousel>>(null)
return (
<BannerStyled>
<Carousel autoplay ref={bannerRel}>
{
banners.map((item, index) =>{
return (
<div key={index} className='banner-item'>
<a href={item.url} target='blank'>
<img className='image' src={item.imageUrl} alt="" />
</a>
</div>
)
})
}
</Carousel>
</BannerStyled>
)
})