一、React 配合 TS 创建项目的步骤
(1)项目创建命令
- 找一处风水宝地
npx create-react-app my-app --template typescript - 切换到目标项目下
cd my-react-app - 项目跑起来
npm start - 下载库声明文件
npm i @types/库名
在命令行中,添加
--template typescript表示创建支持 TS 的项目
(2)主要文件说明
- 多了一个文件:
tsconfig.json。在项目根目录下可以看到它,它就是 TS 的配置文件 - 后缀名有变化:在 src 目录中,文件的后缀有变化,由原来的 .js 变为
.ts或.tsx .tsts 文件的后缀名,.tsx是在 TS 中使用 React 组件时,需要使用该后缀- 在 src 目录中,多了
react-app-env.d.ts文件 .d.ts类型声明文件,用来指定类型文件类型
二、useState hook 在 typescript 中的使用
App.tsx
(1)标准用法-接收泛型参数
import React, { useState } from 'react'
function App() {
// ◆ useState接收一个泛型参数,用于指定初始值的类型
const [name, setName] = useState<string>('小草呀')
const [age, setAge] = useState<number>(18)
const onClickHandler = () => {
// setName(123) 会报错
setName('小花呀')
setAge(20)
}
return (
<div >
<p>我的名字是:{name},今年{age}岁了</p>
<button onClick={onClickHandler}>点我</button>
</div>
)
}
export default App
(2)简便用法-使用类型推论省略泛型参数
import React, { useState } from 'react'
function App() {
// ◆ 由于 typescript 类型推断,useState的泛型参数可以省略
const [name, setName] = useState('小草呀')
const [age, setAge] = useState(18)
const onClickHandler = () => {
setName('小花呀')
setAge(20)
}
return (
<div >
<p>我的名字是:{name},今年{age}岁了</p>
<button onClick={onClickHandler}>点我</button>
</div>
)
}
export default App
三、useEffect hook 在 typescript 中的使用
App.tsx
(1)按照 js 的写法会报错
import React, { useEffect, useState } from 'react'
import axios from 'axios'
function App() {
// ◆ 获取 axios 请求回来的数据
useEffect(() => {
axios.get('http://geek.itheima.net/v1_0/channels')
.then(({ data: { data: { channels: res } } }) => {//这里做了三层解构
console.log(res, 'res');
setChannelList(res)
})
}, [])
// ◆ 按照 js 的写法会报错
const [channelList, setChannelList] = useState([])
return (
<div >
<ul>
{channelList.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
)
}
export default App
报错1:需要下载库声明文件 npm i @types/axios
报错2:因为初始值是 [], tsc 还没聪明到可以推断出你 axios 请求回来的数据类型
(2)解决方式
第一种:提前给初始值,便于类型推导(有类型提示)
import React, { useEffect, useState } from 'react'
import axios from 'axios'
function App() {
// ◆ 获取 axios 请求回来的数据
useEffect(() => {
axios.get('/channels')
.then(({ data: { data: { channels: res } } }) => {//这里做了三层解构
console.log(res, 'res');
setChannelList(res)
})
}, [])
// ◆ 第一种:提前给初始值,便于类型推导(有类型提示)
const [channelList, setChannelList] = useState([{ id: 1, name: 'test' }])
return (
<div >
<ul>
{channelList.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
)
}
export default App
第二种:补充泛型参数:any 大法(没有类型提示)
import React, { useEffect, useState } from 'react'
import axios from 'axios'
function App() {
// ◆ 获取 axios 请求回来的数据
useEffect(() => {
axios.get('/channels')
.then(({ data: { data: { channels: res } } }) => {//这里做了三层解构
console.log(res, 'res')
setChannelList(res)
})
}, [])
// ◆ 第二种:补充泛型参数:any大法(没有类型提示)
const [channelList, setChannelList] = useState<any[]>([])
return (
<div >
<ul>
{channelList.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
)
}
export default App
第三种:补充泛型参数,按接口返回值自定义类型(有类型提示)
import React, { useEffect, useState } from 'react'
import axios from 'axios'
function App() {
// ◆ 获取 axios 请求回来的数据
useEffect(() => {
axios.get('/channels')
.then(({ data: { data: { channels: res } } }) => {//这里做了三层解构
console.log(res, 'res')
setChannelList(res)
})
}, [])
// ◆ 第三种:补充泛型参数,按接口返回值自定义类型(有类型提示)
type ChannelType = { id: number, name: string }
const [channelList, setChannelList] = useState<ChannelType[]>([])
return (
<div >
<ul>
{channelList.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
)
}
export default App
四、useRef hook 在 typescript 中的使用
(1)按照 js 的写法会报错
只能获取 DOM ,不能获取表单元素的值,这里是非受控组件
import React, { useRef } from 'react'
function App() {
// ◆ 按照 js 的写法,会报错
const inpRef = useRef(null)
const onClickHandler = () => {
// 1.可以获取 DOM
console.log(inpRef.current)
// 2.此处会报错
console.log(inpRef.current.value)
}
return (
<div >
<input type="text" ref={inpRef} />
<button onClick={onClickHandler}>非受控组件获取 input 的值</button>
</div>
)
}
(2)解决方式
第一种:使用泛型 + 类型收窄(条件判断)
import React, { useRef } from 'react'
function App() {
// ◆ 第一种:使用泛型 + 类型收窄(条件判断)
const inpRef = useRef<HTMLInputElement>(null)
const onClickHandler = () => {
if (inpRef.current) {
console.log(inpRef.current.value)
}
}
return (
<div >
<input type="text" ref={inpRef} />
<button onClick={onClickHandler}>点我呀</button>
</div>
)
}
注意:使用
useRef操作DOM,需要明确指定所操作的 DOM 的具体的类型,否则 current 属性会是 null
第二种:使用泛型 + 非空断言(对象!.属性)
import React, { useRef } from 'react'
function App() {
// ◆ 第二种:使用泛型 + 非空断言(对象!.属性)
const inpRef = useRef<HTMLInputElement>(null)
const onClickHandler = () => {
if (inpRef.current) {
console.log(inpRef.current!.value)
}
}
return (
<div >
<input type="text" ref={inpRef} />
<button onClick={onClickHandler}>点我呀</button>
</div>
)
}
注意:非空断言一定要确保有该属性才能使用,不然使用非空断言会导致 bug
五、关于 ReactDOM.render 报错问题
很不巧,react 版本于今日更新到 18.0.0 了,所以你知道控制台为什么会报错了吗?
修改入口文件 index.tsx
import React from 'react'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'
import { createRoot } from 'react-dom/client'
const container = document.getElementById('root') as Element
const root = createRoot(container)
root.render(<App />)
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()