學(xué)習(xí)內(nèi)容來(lái)源:React + React Hook + TS 最佳實(shí)踐-慕課網(wǎng)
相對(duì)原教程,我在學(xué)習(xí)開(kāi)始時(shí)(2023.03)采用的是當(dāng)前最新版本:
項(xiàng) | 版本 |
---|---|
react & react-dom | ^18.2.0 |
react-router & react-router-dom | ^6.11.2 |
antd | ^4.24.8 |
@commitlint/cli & @commitlint/config-conventional | ^17.4.4 |
eslint-config-prettier | ^8.6.0 |
husky | ^8.0.3 |
lint-staged | ^13.1.2 |
prettier | 2.8.4 |
json-server | 0.17.2 |
craco-less | ^2.0.0 |
@craco/craco | ^7.1.0 |
qs | ^6.11.0 |
dayjs | ^1.11.7 |
react-helmet | ^6.1.0 |
@types/react-helmet | ^6.1.6 |
react-query | ^6.1.0 |
@welldone-software/why-did-you-render | ^7.0.1 |
@emotion/react & @emotion/styled | ^11.10.6 |
具體配置、操作和內(nèi)容會(huì)有差異,“坑”也會(huì)有所不同。。。
一、項(xiàng)目起航:項(xiàng)目初始化與配置
- 【實(shí)戰(zhàn)】 一、項(xiàng)目起航:項(xiàng)目初始化與配置 —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級(jí)項(xiàng)目(一)
二、React 與 Hook 應(yīng)用:實(shí)現(xiàn)項(xiàng)目列表
- 【實(shí)戰(zhàn)】 二、React 與 Hook 應(yīng)用:實(shí)現(xiàn)項(xiàng)目列表 —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級(jí)項(xiàng)目(二)
三、TS 應(yīng)用:JS神助攻 - 強(qiáng)類(lèi)型
1.TS 的必要性
作為正常人,我們?cè)陂_(kāi)發(fā)過(guò)程中難免會(huì)犯以下錯(cuò)誤:
- 變量名寫(xiě)錯(cuò)
- 參數(shù)少傳、多傳
- 數(shù)組或?qū)ο笞兞繉哟闻e(cuò)
相對(duì) JS
在運(yùn)行時(shí)(runtime)才會(huì)發(fā)現(xiàn)錯(cuò)誤,TS
可以幫助我們?cè)?靜態(tài)代碼 中及時(shí)定位錯(cuò)誤,將 弱類(lèi)型 的 JS
轉(zhuǎn)為 強(qiáng)類(lèi)型 的 TS
能夠極大地降低我們編碼過(guò)程中的誤碼率
2.代碼更改
將項(xiàng)目中 src
下 js
文件后綴改為 ts
,jsx
文件后綴改為 tsx
,并對(duì)文件代碼做如下修改:
- 有參數(shù)的組件使用
interface
聲明參數(shù)類(lèi)型 - 公用類(lèi)型的可以導(dǎo)出+引入
- 不明確類(lèi)型的顯性賦予
unknow
類(lèi)型 (嚴(yán)格版any
) - 不確定參數(shù)是否會(huì)傳的使用
?:
賦予類(lèi)型 - 用泛型來(lái)規(guī)范類(lèi)型
更多
ts
知識(shí)學(xué)習(xí)可見(jiàn):【筆記】TS入門(mén)
更改后的文件如下:
src\utils\index.ts
import { useEffect, useState } from "react";
export const isFalsy = (val: unknown) => (val === 0 ? false : !val);
// 在函數(shù)里,不可用直接賦值的方式改變傳入的引用類(lèi)型變量
export const cleanObject = (obj: object) => {
const res = { ...obj };
Object.keys(res).forEach((key) => {
//@ts-ignore
const val = res[key];
if (isFalsy(val)) {
//@ts-ignore
delete res[key];
}
});
return res;
};
export const useMount = (cbk: () => void) => useEffect(() => cbk(), []);
/**
* @param { 值 } val
* @param { 延時(shí):默認(rèn) 1000 } delay
* @returns 在某段時(shí)間內(nèi)多次變動(dòng)后最終拿到的值(delay 延遲的是存儲(chǔ)在隊(duì)列中的上一次變化)
*/
export const useDebounce = <V>(val: V, delay: number = 1000) => {
// V 泛型,表示傳入與返回類(lèi)型相同
const [tempVal, setTempVal] = useState(val);
useEffect(() => {
// 每次在 val 變化后,設(shè)置一個(gè)定時(shí)器
const timeout = setTimeout(() => setTempVal(val), delay);
// 每次在上一個(gè) useEffect 處理完以后再運(yùn)行(useEffect 的天然功能即是在運(yùn)行結(jié)束的 return 函數(shù)中清除上一個(gè)(同一) useEffect)
return () => clearTimeout(timeout);
}, [val, delay]);
return tempVal;
};
src\screens\ProjectList\index.jsx
import { SearchPanel } from "./components/SearchPanel";
import { List } from "./components/List";
import { useEffect, useState } from "react";
import { cleanObject, useDebounce, useMount } from "utils";
import * as qs from "qs";
const apiUrl = process.env.REACT_APP_API_URL;
export const ProjectListScreen = () => {
const [users, setUsers] = useState([]);
const [param, setParam] = useState({
name: "",
personId: "",
});
// 對(duì) param 進(jìn)行防抖處理
const lastParam = useDebounce(param);
const [list, setList] = useState([]);
useEffect(() => {
fetch(
// name=${param.name}&personId=${param.personId}
`${apiUrl}/projects?${qs.stringify(cleanObject(lastParam))}`
).then(async (res) => {
if (res.ok) {
setList(await res.json());
}
});
}, [lastParam]);
useMount(() => {
fetch(`${apiUrl}/users`).then(async (res) => {
if (res.ok) {
setUsers(await res.json());
}
});
});
return (
<div>
<SearchPanel users={users} param={param} setParam={setParam} />
<List users={users} list={list} />
</div>
);
};
src\screens\ProjectList\components\List.jsx
import { User } from "./SearchPanel";
interface Project {
id: string;
name: string;
personId: string;
star: boolean;
organization: string;
}
interface ListProps {
users: User[];
list: Project[];
}
export const List = ({ users, list }: ListProps) => {
return (
<table>
<thead>
<tr>
<th>名稱</th>
<th>負(fù)責(zé)人</th>
</tr>
</thead>
<tbody>
{list.map((project) => (
<tr key={project.id}>
<td>{project.name}</td>
{/* undefined.name */}
<td>
{users.find((user) => user.id === project.personId)?.name ||
"未知"}
</td>
</tr>
))}
</tbody>
</table>
);
};
src\screens\ProjectList\components\SearchPanel.jsx
export interface User {
id: string;
name: string;
email: string;
title: string;
organization: string;
}
interface SearchPanelProps {
users: User[];
param: {
name: string;
personId: string;
};
setParam: (param: SearchPanelProps["param"]) => void;
}
export const SearchPanel = ({ users, param, setParam }: SearchPanelProps) => {
return (
<form>
<div>
{/* setParam(Object.assign({}, param, { name: evt.target.value })) */}
<input
type="text"
value={param.name}
onChange={(evt) =>
setParam({
...param,
name: evt.target.value,
})
}
/>
<select
value={param.personId}
onChange={(evt) =>
setParam({
...param,
personId: evt.target.value,
})
}
>
<option value="">負(fù)責(zé)人</option>
{users.map((user) => (
<option key={user.id} value={user.id}>
{user.name}
</option>
))}
</select>
</div>
</form>
);
};
src\App.tsx
import "./App.css";
import { ProjectListScreen } from "screens/ProjectList";
function App() {
return (
<div className="App">
<ProjectListScreen />
</div>
);
}
export default App;
拓展學(xué)習(xí):文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-511687.html
- 【筆記】TS 泛型
- 【實(shí)戰(zhàn)】用 Custom Hook + TS泛型實(shí)現(xiàn) useArray
部分引用筆記還在草稿階段,敬請(qǐng)期待。。。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-511687.html
到了這里,關(guān)于【實(shí)戰(zhàn)】三、TS 應(yīng)用:JS神助攻 - 強(qiáng)類(lèi)型 —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級(jí)項(xiàng)目(三)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!