TS与React结合小细节
零碎
创建项目:
create-react-app 名字 --template typescriptrouter中需要创建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>
  )
})