useTypescript-React Hooks和TypeScript完全指南

核心提示引言React v16.8 引入了 Hooks,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。这些功能可以在应用程序中的各个组件之间使用,从而易于共享逻辑。Hook 令人兴奋并迅速被采用,React

引言React v16.8 引入了 Hooks,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。这些功能可以在应用程序中的各个组件之间使用,从而易于共享逻辑。

Hook 令人兴奋并迅速被采用,React 团队甚至想象它们最终将替换类组件。

以前在 React 中,共享逻辑的方法是通过高阶组件和 props 渲染。Hooks 提供了一种更简单方便的方法来重用代码并使组件可塑形更强。本文将展示 Typescript 与 React 集成后的一些变化,以及如何将类型添加到 Hooks 以及你的自定义 Hooks 上。

引入 Typescript 后的变化有状态组件API 对应为:React.Componentclass MyComponent extends React.Component { ...以下是官网的一个例子,创建 Props 和 State 接口,Props 接口接受 name 和 enthusiasmLevel 参数,State 接口接受 currentEnthusiasm 参数:import * as React from "react";export interface Props { name: string; enthusiasmLevel : number;}interface State { currentEnthusiasm: number;}class Hello extends React.Component { constructor {super;this.state = { currentEnthusiasm: props.enthusiasmLevel || 1 }; } onIncrement = => this.updateEnthusiasm; onDecrement = => this.updateEnthusiasm; render {const { name } = this.props;if { throw new Error;}return } ); } updateEnthusiasm {this.setState; }}export default Hello;function getExclamationMarks { return Array.join;}Typescript 可以对 JSX 进行解析,充分利用其本身的静态检查功能,使用泛型进行 Props、 State 的类型定义。

定义后在使用 this.state 和 this.props 时可以在编辑器中获得更好的智能提示,并且会对类型进行检查。react 规定不能通过

http://

this.props.xxx

http://

this.state.xxx

直接进行修改,所以可以通过 readonly 将 State 和 Props 标记为不可变数据:interface Props { readonly number: number;}interface State { readonly color: string;}export class Hello extends React.Component { someMethod {this.props.number = 123; // Error: props 是不可变的this.state.color = 'red'; // Error: 你应该使用 this.setState }}无状态组件API 对应为:// SFC: stateless function componentsconst List: React.SFC = props => null// v16.8起,由于hooks的加入,函数式组件也可以使用state,所以这个命名不准确。新的react声明文件里,也定义了React.FC类型^_^React.FunctionComponent or React.FC。

const MyComponent: React.FC = ...无状态组件也称为傻瓜组件,如果一个组件内部没有自身的 state,那么组件就可以称为无状态组件。在@types/react已经定义了一个类型type SFC = StatelessComponent先看一下之前无状态组件的写法:import React from 'react'const Button = => 如果采用 ts 来编写出来的无状态组件是这样的:import React, { MouseEvent, SFC } from 'react';type Props = { onClick: void };const Button: SFC = => ;事件处理我们在进行事件注册时经常会在事件处理函数中使用 event 事件对象,例如当使用鼠标事件时我们会通过 clientX、clientY 去获取指针的坐标。

大家可以想到直接把 event 设置为 any 类型,但是这样就失去了我们对代码进行静态检查的意义。

function handleMouseChange { console.log}试想下当我们注册一个 Touch 事件,然后错误的通过事件处理函数中的 event 对象去获取其 clientY 属性的值,在这里我们已经将 event 设置为 any 类型,导致 Typescript 在编译时并不会提示我们错误, 当我们通过 event.clientY 访问时就有问题了,因为 Touch 事件的 event 对象并没有 clientY 这个属性。通过 interface 对 event 对象进行类型声明编写的话又十分浪费时间,幸运的是 React 的声明文件提供了 Event 对象的类型声明。

  • 通用的 React Event Handler
API 对应为:React.ReactEventHandler简单的示例:const handleChange: React.ReactEventHandler = => { ... }nChange={handleChange} ... />
  • 特殊的 React Event Handler
常用 Event 事件对象类型:ClipboardEvent 剪贴板事件对象DragEvent 拖拽事件对象ChangeEvent Change 事件对象KeyboardEvent 键盘事件对象MouseEvent 鼠标事件对象TouchEvent 触摸事件对象WheelEvent 滚轮事件对象AnimationEvent 动画事件对象TransitionEvent 过渡事件对象简单的示例:const handleChange = => { ... }React 元素API 对应为:React.ReactElement or JSX.Element简单的示例:// 表示React元素概念的类型: DOM元素组件或用户定义的复合组件const elementOnly: React.ReactElement = || nent />;React NodeAPI 对应为:React.ReactNode表示任何类型的 React 节点(基本上是 ReactElement + 原始 JS 类型的合集)简单的示例:const elementOrComponent: React.ReactNode = 'string' || 0 || false || null || undefined || || nent />;React CSS 属性API 对应为:React.CSSProperties用于标识 jsx 文件中的 style 对象(通常用于 css-in-js)简单的示例:const styles: React.CSSProperties = { display: 'flex', ...const element = Hooks 登场首先,什么是 Hooks 呢?React 一直都提倡使用函数组件,但是有时候需要使用 state 或者其他一些功能时,只能使用类组件,因为函数组件没有实例,没有生命周期函数,只有类组件才有。

Hooks 是 React 16.8 新增的特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。默认情况下,React 包含 10 个钩子。其中 3 个挂钩被视为是最常使用的“基本”或核心挂钩。还有 7 个额外的“高级”挂钩,这些挂钩最常用于边缘情况。

10 个钩子如下:

  • 基础
    • useState
    • useEffect
    • useContext
  • 高级
    • useReducer
    • useCallback
    • useMemo
    • useRef
    • useImperativeHandle
    • useLayoutEffect
    • useDebugValue
useState with TypescriptAPI 对应为:// 传入唯一的参数: initialState,可以是数字,字符串等,也可以是对象或者数组。// 返回的是包含两个元素的数组:第一个元素,state 变量,setState 修改 state值的方法。const [state, setState] = useState;useState是一个允许我们替换类组件中的 this.state 的挂钩。我们执行该挂钩,该挂钩返回一个包含当前状态值和一个用于更新状态的函数的数组。

状态更新时,它会导致组件的重新 render。下面的代码显示了一个简单的 useState 钩子:import * as React from 'react';const MyComponent: React.FC = => { const [count, setCount] = React.useState; return => setCount}> {count} );};useEffect with TypescriptAPI 对应为:// 两个参数// 第一个是一个函数,是在第一次渲染以及之后更新渲染之后会进行的副作用。这个函数可能会有返回值,倘若有返回值,返回值也必须是一个函数,会在组件被销毁时执行。// 第二个参数是可选的,是一个数组,数组中存放的是第一个函数中使用的某些副作用属性。

用来优化 useEffectuseEffect => { // 需要在componentDidMount执行的内容 return function cleanup { // 需要在componentWillUnmount执行的内容 } }, [])useEffect是用于我们管理副作用(例如 API 调用)并在组件中使用 React 生命周期的。useEffect 将回调函数作为其参数,并且回调函数可以返回一个清除函数。回调将在第一次渲染 和组件更新时内执行,清理函数将组件被销毁内执行。

useEffect => { // 给 window 绑定点击事件 window.addEventListener; return => { // 给 window 移除点击事件 window.addEventListener; }});默认情况下,useEffect 将在每个渲染时被调用,但是你还可以传递一个可选的第二个参数,该参数仅允许您在 useEffect 依赖的值更改时或仅在初始渲染时执行。第二个可选参数是一个数组,仅当其中一个值更改时才会 reRender。如果数组为空,useEffect 将仅在 initial render时调用。

useEffect => { // 使用浏览器API更新文档标题 document.title = `You clicked ${count} times`;}, [count]);// 只有当数组中 count 值发生变化时,才会执行这个useEffect。useContext with TypescriptuseContext允许您利用React context这样一种管理应用程序状态的全局方法,可以在任何组件内部进行访问而无需将值传递为 props。useContext 函数接受一个 Context 对象并返回当前上下文值。当提供程序更新时,此挂钩将触发使用最新上下文值的重新渲染。

import { createContext, useContext } from 'react';props ITheme { backgroundColor: string; color: string;}const ThemeContext = createContextconst themeContext = useContext;useReducer with Typescript对于更复杂的状态,您可以选择将该 useReducer 函数用作的替代 useState。const [state,dispatch] = useReducer(reducer,initialState,init);如果您以前使用过Redux,则应该很熟悉。useReducer接受 3 个参数并返回当前的 state 以及与其配套的 dispatch 方法。

reducer 是如下形式的函数 => newState;initialState 是一个 Javascript 对象;而 init 参数是一个惰性初始化函数,可以让你延迟加载初始状态。这听起来可能有点抽象,让我们看一个实际的例子:const initialState = 0;function reducer { switch {case 'increment': return {number: state.number + 1};case 'decrement': return {number: state.number - 1};default: throw new Error; }}function init{return {number:initialState};}function Counter{const [state, dispatch] = useReducer;return => dispatch}>+ )}看完例子再结合上面 useReducer 的 api 是不是立马就明白了呢?useCallback with TypescriptuseCallback 钩子返回一个 memoized 回调。这个钩子函数有两个参数:第一个参数是一个内联回调函数,第二个参数是一个数组。数组将在回调函数中引用,并按它们在数组中的存在顺序进行访问。

const memoizedCallback = useCallback(()=> {doSomething(a,b); },[ a,b ],);useCallback 将返回一个记忆化的回调版本,它仅会在某个依赖项改变时才重新计算 memoized 值。当您将回调函数传递给子组件时,将使用此钩子。这将防止不必要的渲染,因为仅在值更改时才执行回调,从而可以优化组件。

可以将这个挂钩视为与shouldComponentUpdate生命周期方法类似的概念。useMemo with TypescriptuseMemo返回一个 memoized 值。 传递“创建”。

 
友情链接