cloud setups
- TypeScript Playground with React just if you are debugging types (and reporting issues), not for running code
- CodeSandbox - cloud IDE, boots up super fast
- Stackblitz - cloud IDE, boots up super fast
组件的写法
function
function Hello({ ame }: Props) { return ( <div>Hello, {name}</div> );}export default Hello;
class
class Hello extends React.Component<Props, object> { render() { const { name } = this.props; return ( <div>Hello, {name}</div> ); }}
函数式组件和类组件的不同
React Hooks因为是函数式组件,在异步操作或者应用useCallBack、useEffect、useMemo等API时会造成闭包。
Hooks
1. useState
function Counter({initialCount}) { const [count, setCount] = useState<nubmer>(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> </> );}
2. useReducer
reducer: (state, action) => newState
实用状况:
- state 逻辑较简单且蕴含多个子值
- 下一个 state 依赖于之前的 state
- 会触发深更新的组件做性能优化,因为你能够向子组件传递
dispatch
而不是回调函数 。
const initialState = { count: 0 };type ACTIONTYPE = | { type: "increment"; payload: number } | { type: "decrement"; payload: string };function reducer(state: typeof initialState, action: ACTIONTYPE) { switch (action.type) { case "increment": return { count: state.count + action.payload }; case "decrement": return { count: state.count - Number(action.payload) }; default: throw new Error(); }}function Counter() { const [state, dispatch] = React.useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({ type: "decrement", payload: "5" })}> - </button> <button onClick={() => dispatch({ type: "increment", payload: 5 })}> + </button> </> );}
惰性初始化
将 init
函数作为 useReducer
的第三个参数传入,这样初始 state 将被设置为 init(initialArg)
。
import * as React from "react";import { useReducer } from "react";const initialCount = 0;type ACTIONTYPE = | { type: "increment"; payload: number } | { type: "decrement"; payload: string } | { type: "reset"; payload: number };function init(initialCount: number) { return { count: initialCount };}function reducer(state: {count: number}, action: ACTIONTYPE) { switch (action.type) { case "reset": return init(action.payload); case "increment": return { count: state.count + action.payload }; case "decrement": return { count: state.count - Number(action.payload) }; default: throw new Error(); }}export function Counter() { const [state, dispatch] = useReducer(reducer, initialCount, init); return ( <> Count: {state.count} <button onClick={() => dispatch({ type: "reset", payload: initialCount })} > reset </button> <button onClick={() => dispatch({ type: "decrement", payload: "5" })}> - </button> <button onClick={() => dispatch({ type: "increment", payload: 5 })}> + </button> </> );}
3. useEffect
function DelayedEffect(props: { timerMs: number }) { const { timerMs } = props; useEffect(() => { setTimeout(() => { /* do stuff */ }, timerMs); }, [timerMs]); // better; use the void keyword to make sure you return undefined return null;}
4. useRef
const ref1 = useRef<HTMLElement>(null!);const ref2 = useRef<HTMLElement>(null);const ref3 = useRef<HTMLElement | null>(null);
useRef
返回一个可变的 ref 对象,其 .current
属性被初始化为传入的参数(initialValue
)。返回的 ref 对象在组件的整个生命周期内放弃不变。
function TextInputWithFocusButton() { // initialise with null, but tell TypeScript we are looking for an HTMLInputElement const inputEl = React.useRef<HTMLInputElement>(null); const onButtonClick = () => { // strict null checks need us to check if inputEl and current exist. // but once current exists, it is of type HTMLInputElement, thus it // has the method focus! ✅ if (inputEl && inputEl.current) { inputEl.current.focus(); } }; return ( <> {/* in addition, inputEl only can be used with input elements. Yay! */} <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> );}
Custom Hooks
export function useLoading() { const [isLoading, setState] = React.useState(false); const load = (aPromise: Promise<any>) => { setState(true); return aPromise.finally(() => setState(false)); }; return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[]}
也能够写成:
export function useLoading() { const [isLoading, setState] = React.useState(false); const load = (aPromise: Promise<any>) => { setState(true); return aPromise.finally(() => setState(false)); }; return [isLoading, load] as [ boolean, (aPromise: Promise<any>) => Promise<any> ];}
Enum 类型
尽量避免应用枚举类型,能够用以下代替:
export declare type Position = "left" | "right" | "top" | "bottom";
React+TypeScript Cheatsheets
你真的理解React Hooks吗?