您现在的位置是:网站首页> 编程资料编程资料

在React项目中使用TypeScript详情_React_

2023-05-24 226人已围观

简介 在React项目中使用TypeScript详情_React_

前言:

本文主要记录我如何在React项目中优雅的使用TypeScript,来提高开发效率及项目的健壮性。

项目目录及ts文件划分

由于我在实际项目中大部分是使用umi来进行开发项目,所以使用umi生成的目录来做案例。

. ├── README.md ├── global.d.ts ├── mock ├── package.json ├── src │   ├── assets │   ├── components │   │   └── PublicComA │   │   ├── index.d.ts │   │   ├── index.less │   │   └── index.tsx │   ├── layouts │   ├── models │   ├── pages │   │   ├── PageA │   │   │   ├── index.d.ts │   │   │   ├── index.less │   │   │   └── index.tsx │   │   ├── index.less │   │   └── index.tsx │   └── utils ├── tsconfig.json ├── typings.d.ts └── yarn.lock

在项目根目录下有typings.d.ts和global.d.ts这两个文件, 前者我们可以放置一些全局的导出模块,比如css,less, 图片的导出声明;后者可以放一些全局声明的变量, 接口等, 比如说window下全局变量的声明等。

如下:

// typings.d.ts declare module '*.css'; declare module '*.less'; declare module "*.png"; declare module "*.jpeg"; declare module '*.svg' { export function ReactComponent(props: React.SVGProps): React.ReactElement const url: string export default url }
// global.d.ts interface Window { helloWorld: () => void; }

接下来介绍一下src目录:

  • assets 存放静态资源如图片/视频/音频等, 参与webpack的打包过程
  • layouts 存放公共布局
  • components 存放全局公共组件
  • models dva的models文件夹
  • pages 存放页面的目录, 内部可以有页面组件components, 结构类似于全局的components
  • utils 存放js工具库, 请求库等公共js文件

在pages和components中有存放当前组件/页面所需要的类型和接口声明的index.d.ts。另外如models中的文件由于是每个model私有类型和接口声明,所以可以直接在文件内部去声明。 具体的目录规划如上,可以根据实际项目来做更合理的划分。

在项目中使用TypeScript具体实践

组件声明

  • 函数组件 推荐使用React.FC来表示函数类型,当使用该类型定义组件时,props中会默认带有children属性。
interface IProps { count: number } const App: React.FC = (props) => { const {count} = props; return ( 
count: {count}
); }
  • 类组件 类组件接受两个参数,第一个是props的定义,第二个是state的定义,如果使用React.PureComponent定义组件,则还有第三个参数,表示getSnapshotBeforeUpdate的返回值。
interface IProps { name: string; } interface IState { count: number; } class App extends React.Component { state = { count: 0 }; render() { return ( 
{this.state.count} {this.props.name}
); } }

React Hooks使用

useState

声明定义:

function useState(initialState: S | (() => S)): [S, Dispatch>]; // convenience overload when first argument is omitted /** * Returns a stateful value, and a function to update it. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#usestate */ function useState(): [S | undefined, Dispatch>]; /** * An alternative to `useState`. * * `useReducer` is usually preferable to `useState` when you have complex state logic that involves * multiple sub-values. It also lets you optimize performance for components that trigger deep * updates because you can pass `dispatch` down instead of callbacks. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#usereducer */

如果初始值能够体现出类型,那么可以不用手动声明类型,TS会自动推断出类型。如果初始值为null或者undefined则需要通过泛型显示声明类型。

如下:

const [count, setCount] = useState(1); const [user, setUser] = useState(null);

useRef

声明定义:

 function useRef(initialValue: T): MutableRefObject; // convenience overload for refs given as a ref prop as they typically start with a null value /** * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument * (`initialValue`). The returned object will persist for the full lifetime of the component. * * Note that `useRef()` is useful for more than the `ref` attribute. It's handy for keeping any mutable * value around similar to how you'd use instance fields in classes. * * Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type * of the generic argument. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#useref */

使用该Hook时,要根据使用场景来判断传入泛型类型,如果是获取DOM节点,则传入对应DOM类型即可;如果需要的是一个可变对象,则需要在泛型参数中包含'| null'。

如下:

// 不可变DOM节点,只读 const inputRef = useRef(null); // 可变,可重新复制 const idRef = useRef(null); idRef.current = "abc";

useCallback

声明定义:

 function useCallback any>(callback: T, deps: DependencyList): T; /** * `useMemo` will only recompute the memoized value when one of the `deps` has changed. * * Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in * the second argument. * * ```ts * function expensive () { ... } * * function Component () { * const expensiveResult = useMemo(expensive, [expensive]) * return ... * } * ``` * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#usememo */

useCallback会根据返回值自动推断出类型,如果传入的参数不指定类型,则会默认为any,所以为了严谨和可维护性,一定要指定入参的类型。也可以手动传入泛型指定函数类型。

如下:

// 会自动推导出类型: (a: number, b: number) => number; const add = useCallback((a: number, b: number) => a + b, [a, b]) // 传入泛型,则指定函数类型 const toggle = useCallback<(a: number) => number>((a: number) => a * 2, [a])

useMemo

声明定义:

function useMemo(factory: () => T, deps: DependencyList | undefined): T; /** * `useDebugValue` can be used to display a label for custom hooks in React DevTools. * * NOTE: We don't recommend adding debug values to every custom hook. * It's most valuable for custom hooks that are part of shared libraries. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#usedebugvalue */

useMemo和useCallback类似,只是定义类型为具体返回值的类型,而不是函数的类型。

如下:

// 会自动推导出类型: number; const add = useCallback((a: number, b: number) => a + b, [a, b]) // 传入泛型,则指定函数类型 const toggle = useCallback((a: number) => a * 2, [a])

useContext

声明定义:

function useContext(context: Context/*, (not public API) observedBits?: number|boolean */): T; /** * Returns a stateful value, and a function to update it. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#usestate */

useContext会根据传入的上下文对象自动推导出context的类型,当然也可以使用泛型来设置context的类型,

如下:

interface ITheme { color: string; } const ThemeContext = React.createContext({ color: "red" }); // 自动推导出类型为ITheme const theme = useContext(ThemeContext); // 等同于const theme = useContext(ThemeContext);

useReducer

声明定义:

function useReducer>( reducer: R, initialState: ReducerState, initializer?: undefined ): [ReducerState, Dispatch>]; /** * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument * (`initialValue`). The returned object will persist for the full lifetime of the component. * * Note that `useRef()` is useful for more than the `ref` attribute. It's handy for keeping any mutable * value around similar to how you'd use instance fields in classes. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#useref */

上面只列出了一种类型定义,我在项目中也是使用这种定义去指定useReducer的类型。普通的案例如下:

type StateType = { name: string; age: number; } type Actions = { type: 'Change_Name'; payload: string; } | { type: 'Change_Age'; payload: number; } const initialState = { name: '小明', age: 18 } const reducerAction: Reducer = ( state, action, ) => { switch (action.type) { case 'Change_Name': return { ...state, name: action.payload }; case 'Change_Age': return { ...state, age: action.payload }; default: return state; } }; function Index() { const [state, dispatch] = useReducer(reducerAction, initialState); return ( 
姓名:{state.name}
年龄:{state.age}
); }

可以看到,这样能够得到正确的类型推断,但是略微繁琐。

<