3分鐘掌握hook在typescript中的姿勢
阿新 • • 發佈:2021-12-08
3分鐘掌握hook在typescript中的姿勢
2020-01-14閱讀1.1K0
hook
結合typescript可以說是很香了。本文主要介紹hook
結合typescript 如何使用,享受ts帶給我們的編輯器提示和型別約束
useState
useState
如果初始值不是null/undefined
的話,是具備型別推導能力的,根據傳入的初始值推斷出型別;初始值是 null/undefined
的話則需要傳遞型別定義才能進行約束。一般情況下,還是推薦傳入型別(通過useState的第一個泛型引數)。
// 這裡ts可以推斷 value的型別並且能對setValue函式呼叫進行約束
const [value, setValue] = useState(0);
interface MyObject {
foo: string;
bar?: number;
}
// 這裡需要傳遞MyObject才能約束 value, setValue
// 一般情況下推薦傳入型別
const [value, setValue] = useState<MyObject>(null);
useContext
useContext
一般根據傳入的Context的值就可以推斷出返回值。不需要顯示傳遞型別
type Theme = 'light' | 'dark';
// 我們在createContext就傳了型別了
const ThemeContext = createContext<Theme>('dark');
const App = () => (
<ThemeContext.Provider value="dark">
<MyComponent />
</ThemeContext.Provider>
)
const MyComponent = () => {
// useContext根據ThemeContext推斷出型別,這裡不需要顯示傳
const theme = useContext(ThemeContext);
return <div>The theme is {theme}</div>;
useEffect
useLayoutEffect
沒有返回值,無需傳遞型別
useCallback
useMemo
useMemo
無需傳遞型別,根據函式的返回值就能推斷出型別
useCallback
無需傳遞型別,根據函式的返回值就能推斷出型別。但是注意函式的入參需要定義型別,不然就是推斷為any了!
const value = 10;
// 推斷出result是number型別
const result = useMemo(() => value * 2, [value]);
const multiplier = 2;
// 推斷出 (value: number) => number
// 注意函式入參value需要定義型別
const multiply = useCallback((value: number) => value * multiplier, [multiplier]);
useRef
useRef
傳非空初始值的時候可以推斷型別,同樣也可以通過傳入第一個泛型引數來定義型別,約束ref.current
的型別。
const MyInput = () => {
// 這裡約束inputRef是一個html元素
const inputRef = useRef<HTMLInputElement>(null);
return <input ref={inputRef} />
}
// 自動推斷出 myNumberRef.current 是number型別
const myNumberRef = useRef(0);
myNumberRef.current += 1;
useReducer
只需要對傳入useReducer
的reducer函式的入參state
和action
進行型別約束就能夠推斷出來
interface State {
value: number;
}
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'incrementAmount'; amount: number };
const counterReducer = (state: State, action: Action) => {
switch (action.type) {
case 'increment':
return { value: state.value + 1 };
case 'decrement':
return { value: state.value - 1 };
case 'incrementAmount':
return { value: state.value + action.amount };
default:
throw new Error();
}
};
// 這裡可以推斷出state為State型別
const [state, dispatch] = useReducer(counterReducer, { value: 0 });
//能夠約束傳入dispatch的引數,符合Action型別
dispatch({ type: 'increment' });
dispatch({ type: 'decrement' });
dispatch({ type: 'incrementAmount', amount: 10 });
// TypeScript compilation error
dispatch({ type: 'invalidActionType' });
useImperativeHandle
useImperativeHandle
一般比較少用,一般用來選擇函式元件對外暴露ref屬性被呼叫,需要配合forwardRef
使用。
如下例子。需要定義對外暴露的介面MyInputHandles
,函式元件使用React.RefForwardingComponent
對外暴露的介面呼叫作為泛型引數。然後就會得到約束了
// MyInputHandles 需要給父元件的useRef作為型別使用 和 RefForwardingComponent作為泛型引數傳入約束
export interface MyInputHandles {
focus(): void;
}
// 使用RefForwardingComponent 型別進行定義元件,第一個泛型引數是對外暴露的handle,第二個是Props
const MyInput: RefForwardingComponent<MyInputHandles, MyInputProps> = (
props,
ref
) => {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
// 這裡的返回會自動使用MyInputHandles進行型別約束
focus: () => {
if (inputRef.current) {
inputRef.current.focus();
}
},
}));
return <input {...props} ref={inputRef} />;
};
// 函式元件必須使用forwardRef才能讓外部元件使用該元件的ref
export default forwardRef(MyInput);
// 父元件
const Autofocus = () => {
// 能夠約束 myInputRef.current的型別
const myInputRef = useRef<MyInputHandles>(null);
useEffect(() => {
if (myInputRef.current) {
myInputRef.current.focus();
}
});
return <MyInput ref={myInputRef} />
}
參考:
本文參與騰訊雲自媒體分享計劃,歡迎正在閱讀的你也加入,一起分享。
漫思