The introduction
React V16.8 introduces Hooks, which allow you to use state and other React features without writing classes. These capabilities can be used across components in an application, making it easy to share logic. Hooks were exciting and quickly adopted, and the React team even imagined they would eventually replace class components.
Previously in React, the way to share logic was to render through higher-order components and props. Hooks provide an easier and more convenient way to reuse code and make components more shapeable.
This article will show you some of the changes after the TypeScript and React integration, and how to add types to Hooks and your own custom Hooks.
Changes since the introduction of Typescript
Stateful Component (ClassComponent)
The API corresponds to:
React.Component<P, S>
class MyComponent extends React.Component<Props.State> {...Copy the code
The Props interface accepts the name and the enthusiasmLevel parameters, and the State interface accepts the currentEnthusiasm parameter.
import * as React from "react";
export interface Props {
name: string; enthusiasmLevel? : number; } interface State {currentEnthusiasm: number;
}
class Hello extends React.Component<Props.State> {
constructor(props: Props) {
super(props);
this.state = { currentEnthusiasm: props.enthusiasmLevel || 1 };
}
onIncrement = (a)= > this.updateEnthusiasm(this.state.currentEnthusiasm + 1);
onDecrement = (a)= > this.updateEnthusiasm(this.state.currentEnthusiasm - 1);
render() {
const { name } = this.props;
if (this.state.currentEnthusiasm <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(this.state.currentEnthusiasm)}
</div>
<button onClick={this.onDecrement}>-</button>
<button onClick={this.onIncrement}>+</button>
</div>
);
}
updateEnthusiasm(currentEnthusiasm: number) {
this.setState({ currentEnthusiasm }); }}export default Hello;
function getExclamationMarks(numChars: number) {
return Array(numChars + 1).join('! ');
}
Copy the code
TypeScript parses JSX, taking advantage of its static checking capabilities and using generics to type functions and State. When you use this.state and this.props, you get better intelligence in the editor and check the type.
React: this.props. XXX and this.state. XXX cannot be modified directly, so you can use readonly to mark state and props as immutable:
interface Props {
readonly number: number;
}
interface State {
readonly color: string;
}
export class Hello extends React.Component<Props, State> {
someMethod() {
this.props.number = 123; // Error: props is immutable
this.state.color = 'red'; // Error: you should use this.setState()}}Copy the code
StatelessComponent
The API corresponds to:
// SFC: stateless function components
const List: React.SFC<IProps> = props= > null
// as of v16.8, functional components can also use state due to hooks, so this name is incorrect. The react declaration file also defines the react.FC type ^_^The React. FunctionComponent < P > the or React. FC < P >.const MyComponent: React.FC<Props> = ...
Copy the code
Stateless components are also called dumb components. A component can be called stateless if it does not have its own state inside it. SFC
= StatelessComponent is defined at @types/react
Let’s take a look at the stateless component written earlier:
import React from 'react'
const Button = ({ onClick: handleClick, children }) = > (
<button onClick={handleClick}>{children}</button>
)
Copy the code
A stateless component written in TS looks like this:
import React, { MouseEvent, SFC } from 'react';
type Props = { onClick(e: MouseEvent<HTMLElement>): void };
const Button: SFC<Props> = ({ onClick: handleClick, children }) = > (
<button onClick={handleClick}>{children}</button>
);
Copy the code
The event processing
We often use event event objects in event handlers when registering events. For example, when using mouse events, we get the coordinates of Pointers through clientX and clientY.
You could have thought of setting the event to type ANY, but that would have lost the point of statically checking our code.
function handleMouseChange (event: any) {
console.log(event.clientY)
}
Copy the code
Imagine registering a Touch event and mistakenly getting the value of its clientY property from the event object in the event handler. In this case, we’ve set the event to any, so TypeScript doesn’t tell us when we compile. There is a problem when we access it via event.clientY, because the event object for the Touch event does not have clientY.
Writing a type declaration for an event object through an interface is a waste of time. Fortunately, the React declaration file provides a type declaration for the event object.
- Common React Event Handler
The API corresponds to:
React.ReactEventHandler<HTMLElement>
Copy the code
A simple example:
const handleChange: React.ReactEventHandler<HTMLInputElement> = (ev) = >{... } <input onChange={handleChange} ... />Copy the code
- Special React Event Handler
Common Event Event object types:
DragEvent<T = Element> DrageEvent <T = Element> Change KeyboardEvent<T = Element> keyboard event object MouseEvent<T = Element> MouseEvent object TouchEvent<T = Element> TouchEvent object WheelEvent<T = Element> WheelEvent object AnimationEvent<T = Element> AnimationEvent object TransitionEvent<T = Element> TransitionEvent objectCopy the code
A simple example:
const handleChange = (ev: React.MouseEvent<HTMLDivElement>) = >{... } <div onMouseMove={handleChange} ... />Copy the code
The React elements
The API corresponds to:
React.ReactElement<P> or JSX.Element
Copy the code
A simple example:
// Represents the type of React element concept: DOM element component or user-defined composite component
const elementOnly: React.ReactElement = <div /> || <MyComponent />;
Copy the code
React Node
The API corresponds to:
React.ReactNode
Copy the code
Represents any type of React node (basically a set of ReactElement + original JS types)
A simple example:
const elementOrComponent: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />;
Copy the code
The React CSS properties
The API corresponds to:
React.CSSProperties
Copy the code
Used to identify style objects in JSX files (typically for CSS-in-JS)
A simple example:
const styles: React.CSSProperties = { display: 'flex'. const element =<div style={styles} .
Copy the code
Hooks on
First, what are Hooks?
React has always advocated using function components, but sometimes when you need to use state or other functions, you have to use class components. Function components have no instances, no lifecycle functions, only class components.
Hooks are new in React 16.8 that allow you to use state and other React features without writing a class.
React contains 10 hooks by default. Three of these hooks are considered to be the most commonly used “basic” or core hooks. There are also seven additional “advanced” hooks, which are most commonly used in edge cases. The 10 hooks are as follows:
- basis
useState
useEffect
useContext
- senior
useReducer
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
useDebugValue
useState with TypeScript
The API corresponds to:
// Pass in a unique argument: initialState, which can be a number, string, etc., or an object or array.
// Returns an array containing two elements: the first element, the state variable, and the setState method to modify the state value.
const [state, setState] = useState(initialState);
Copy the code
UseState is a hook that allows us to replace this.state in a class component. We execute the hook, which returns an array containing the value of the current state and a function to update the state. When the status is updated, it causes the component to rerender. The following code shows a simple useState hook:
import * as React from 'react';
const MyComponent: React.FC = (a)= > {
const [count, setCount] = React.useState(0);
return (
<div onClick={()= > setCount(count + 1)}>
{count}
</div>
);
};
Copy the code
useEffect with TypeScript
The API corresponds to:
// Two parameters
// The first is a function that is a side effect of the first render (componentDidMount) and subsequent updates to the render. This function may have a return value, and if it does, it must be a function that is executed when the component is destroyed (componentWillUnmount).
// The second argument, optional, is an array containing some of the side effects used in the first function. Used to optimize useEffect
useEffect((a)= > { Return function cleanup() {cleanup at componentDidMount}}, [])
Copy the code
UseEffect is used to manage side effects (such as API calls) and use the React lifecycle in components. UseEffect takes a callback function as its argument, and the callback function can return a cleanup function. The callback will be executed within the first render (componentDidMount) and component update (componentDidUpate), and the cleanup function will be executed within componentWillUnmount (componentWillUnmount).
useEffect((a)= > {
// Bind the click event to the window
window.addEventListener('click', handleClick);
return (a)= > {
// Remove the click event for window
window.addEventListener('click', handleClick); }});Copy the code
By default, useEffect will be called on every render, but you can also pass an optional second parameter that allows you to execute only when useEffect depends on a value that changes or only during the initial render. The second optional argument is an array that is reRender(re-rendered) only if one of the values changes. If the array is empty, useEffect will only be called with initial render.
useEffect((a)= > {
// Update document titles using browser apis
document.title = `You clicked ${count} times`;
}, [count]); UseEffect is executed only when the count value in the array changes.
Copy the code
useContext with TypeScript
UseContext allows you to leverage the React Context as a global way to manage application state, accessible from within any component without passing the value as props.
The useContext function takes a Context object and returns the current Context value. When the provider updates, this hook triggers a re-rendering with the latest context value.
import { createContext, useContext } from 'react';
props ITheme {
backgroundColor: string;
color: string;
}
const ThemeContext = createContext<ITheme>({
backgroundColor: 'black'.color: 'white',})const themeContext = useContext<ITheme>(ThemeContext);
Copy the code
useReducer with TypeScript
For more complex states, you can choose to use this useReducer function as an alternative to useState.
const[state, dispatch] = useReducer (reducer, initialState, init);Copy the code
If you’ve used Redux before, you should be familiar with it. The useReducer accepts three parameters (Reducer, initialState, init) and returns the current state and its corresponding dispatch method. Reducer is a function of the following form (state, action) => newState; InitialState is a JavaScript object; The init parameter is an lazy initialization function that lets you lazily load the initial state.
This may sound abstract, but let’s look at a practical example:
const initialState = 0;
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {number: state.number + 1};
case 'decrement':
return {number: state.number - 1};
default:
throw new Error();
}
}
function init(initialState){
return {number:initialState};
}
function Counter(){
const [state, dispatch] = useReducer(reducer, initialState,init);
return (
<>
Count: {state.number}
<button onClick={()= > dispatch({type: 'increment'})}>+</button>
<button onClick={()= > dispatch({type: 'decrement'})}>-</button>
</>)}Copy the code
See the example and then combine the above useReducer API is not immediately clear?
useCallback with TypeScript
The useCallback hook returns a Memoized callback. This hook function takes two arguments: the first argument is an inline callback function, and the second argument is an array. Arrays are referenced in the callback function and accessed in the order in which they exist in the array.
constMemoizedCallback = useCallback (() => {doSomething (a, b); }, [a, b],);Copy the code
UseCallback will return a memorized callback version that recalculates memoized values only when a dependency changes. This hook is used when you pass callback data to a child component. This prevents unnecessary rendering because the callback is performed only when the value changes, allowing the component to be optimized. You can think of this hook as a similar concept to the shouldComponentUpdate lifecycle method.
useMemo with TypeScript
UseMemo returns a memoized value. Pass create function and dependency array. UseMemo recalculates memoized values only when one of the dependencies changes. This optimization helps avoid expensive calculations on each render.
constMemoizedValue = useMemo (() => computeExpensiveValue (a, b), [A, b]);Copy the code
Functions passed by useMemo during rendering run. Don’t do things you wouldn’t normally do when rendering. For example, side effects are useEffect, not useMemo.
If you look at this, you might be thinking, Well, useMemo and useCallback do a little bit of the same thing, but what’s the difference?
- Both useCallback and useMemo can cache references or values to functions.
- On a more granular level, useCallback is a reference to the useMemo cache function, which computes the value of the data.
useRef with TypeScript
The useRef hook allows you to create a ref and access the properties of the underlying DOM node. You can use this method when you need to extract a value from an element or when you need to get dom-related element information (such as its scroll position).
constRefContainer = useRef (initialValue);Copy the code
UseRef returns a mutable ref object whose.current property is initialized as the passed parameter. The returned object persists throughout the life of the component.
function TextInputWithFocusButton() {
const inputEl = useRef<HTMLInputElement>(null);
const onButtonClick = (a)= > {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Copy the code
useImperativeHandle with TypeScript
UseImperativeHandle allows you to customize the instance values exposed to the parent component when using a ref.
useImperativeHandle(ref, createHandle, [inputs])
Copy the code
The useImperativeHandle hook function takes three arguments: a React Ref, a createHandle function, and an optional array to expose to the parent component’s arguments.
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: (a)= >{ inputRef.current.focus(); }}));return <input ref={inputRef} . />;
}
FancyInput = React.forwardRef(FancyInput);
const fancyInputRef = React.createRef();
<FancyInput ref={fancyInputRef}>Click me!</FancyInput>;
Copy the code
useLayoutEffect with TypeScript
Similar to useEffect Hooks, perform side effects. But it is triggered after all DOM updates are complete. It can be used to perform layout-related side effects, such as getting the DOM element width and height, window scrolling distance, and so on.
useLayoutEffect((a)= > { doSomething });
Copy the code
UseEffect is preferred when performing side effects to avoid blocking view updates. UseEffect for side effects that are not DOM related.
import React, { useRef, useState, useLayoutEffect } from 'react';
export default() = > {const divRef = useRef(null);
const [height, setHeight] = useState(50);
useLayoutEffect((a)= > {
// Print out the height of the div after DOM update is complete
console.log('useLayoutEffect: ', divRef.current.clientHeight);
})
return <>
<div ref={ divRef } style={{ background: 'red', height: height}} >Hello</div>
<button onClick={() = >SetHeight (height + 50)}> Change the div height</button>
</>
}
Copy the code
useDebugValue with TypeScript
UseDebugValue is used for debugging custom link (please refer to https://reactjs.org/docs/hooks-custom.html) custom hook tools. It allows you to display labels for custom hook functions in React Dev Tools.
The sample
I’ve built a simple background generic template based on Umi + React +typescript+ ant-Design.
The functions covered are as follows:
- Components - Base Form - ECharts Chart - Forms - Base Form - Step Form - Editor - Console - Error page - 404Copy the code
Combined with Hooks in the react in various scenarios typescript have good practice, everybody can consult, interested in https://github.com/FSFED/Umi-hooks/tree/feature_hook, Of course don’t begrudge your star!!
The last
You can follow my public account of the same name [Front-end Forest], where I will regularly post some cutting-edge articles related to the big front-end and summarize the actual combat in the daily development process. Of course, I am also an active contributor to the open source community, github https://github.com/Jack-cool, welcome star!!