const [isLoading, setIsLoading] = useState<boolean>(false);
複製代碼
若是有複雜數據結構的,可使用 useImmer ,解決深層數據修改,視圖不更新的問題react
簡單的可使用
數組map
方法返回一個新數組,或者深拷貝一下,再設置就好了ios
import { useImmer } from 'use-immer';
export interface TreeDataItemProps {
label: string;
value: string;
children?: TreeDataItemProps[];
}
// ...
const [treeData, setTreeData] = useImmer<TreeDataItemProps>({
label: '',
value: '',
children: []
});
// ...
複製代碼
若是 useState
不少,能夠把相關的 state 改爲一個 對象的 useReducer
寫法git
import React, { useReducer } from 'react';
export interface CountStateProps {
count: number;
}
export interface CountActionProps {
type: 'increment' | 'decrement' | 'set';
value?: number;
}
const countReducer = (state: CountStateProps, { type, value }: CountActionProps) => {
switch (type) {
case 'increment':
return {
count: state.count + 1,
};
case 'decrement':
return {
count: state.count - 1,
};
case 'set':
return {
count: value || state.count,
};
default:
return state;
}
};
const initialCountState: CountStateProps = { count: 0 };
const Count: React.FC = props => {
const [countState, countDispatch] = useReducer(countReducer, initialCountState);
return (
<div>
<p>{countState.count}</p>
<button type="button" onClick={() => countDispatch({ type: 'increment' })}>
increment
</button>
<button type="button" onClick={() => countDispatch({ type: 'decrement' })}>
decrement
</button>
<button type="button" onClick={() => countDispatch({ type: 'set', value: 1 })}>
set
</button>
</div>
);
};
export default Count;
複製代碼
useEffect(() => fn, [deps]);
複製代碼
fn:回調函數github
回調函數內返回一個函數,且依賴項爲空數組 []
時,這個函數會在當前組件卸載時執行typescript
好比一些 事件監聽/定時器 能夠這裏取消npm
deps:依賴項axios
[]
:fn 只會在當前頂層函數 mount 後執行一次[deps]
: deps 任意項變化後,都會執行 fnuseEffect(() => {
console.log('mount 時會打印');
return () => {
console.log('unmount 時會打印');
};
}, []);
useEffect(() => {
console.log('每次 State 變化都會打印');
});
useEffect(() => {
console.log('Mount 後打印一次');
}, []);
useEffect(() => {
console.log('deps 任意項變化後都會打印');
}, [deps]);
複製代碼
監聽依賴的變化,執行回調函數,回調函數的返回值 做爲 useMemo
的返回值,能夠緩存結果,相似 Vue 計算屬性 computedapi
const memorized = useMemo(() => {
console.log('deps changed');
return 'newValue';
}, [deps]);
複製代碼
監聽依賴的變化,執行新的回調函數;依賴不變化則不會執行數組
const fn = useCallback(() => {
console.log('deps changed');
}, [deps]);
複製代碼
useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化爲傳入的參數(initialValue)。返回的 ref 對象在組件的整個生命週期內保持不變。緩存
const InputRef = useRef(null);
InputRef.focus();
<input ref={inputRef} /> 複製代碼
useRef 能夠經過 *.current
來存儲/獲取變量值,改變不會觸發頁面更新;
能夠看 自定義 Hook
usePrevState
const value = useRef(null);
value.current = newVal;
複製代碼
函數組件沒有 ref
;若是要在父組件經過 ref
,須要使用 useImperativeHandle
+ React.forwardRef
實現;useImperativeHandle
回調函數的返回值,能夠被父組件經過 ref
調用
無論是
createRef
仍是useRef
都不是動態的;即便被引用的 子組件更新了,也不會從新獲取新的 ref
import React, { useState, useImperativeHandle } from 'react';
export interface CountRefProps {
count: number;
setCount: React.Dispatch<React.SetStateAction<number>>;
}
const Count: React.FC = (props, ref) => {
const [count, setCount] = useState(0);
useImperativeHandle(ref, () => ({
count,
setCount,
}));
return <div>{count}</div>;
};
export default React.forwardRef(Count);
複製代碼
import React, { useRef } from 'react';
import Count, { CountRefProps } from './Count';
const Counter: React.FC = () => {
const countRef = useRef<CountRefProps>(null);
const onClick = () => {
console.log(countRef.current!.count); // 0
// 調用 Count 的 setCount 方法,使 Count 視圖更新
countRef.current!.setCount(1);
// 子組件更新了,可是這裏仍是一開始的 ref,不會自動更新的
console.log(countRef.current!.count); // 0
};
return (
<div>
<Count ref={countRef} />
<button type="button" onClick={onClick}>
setCount
</button>
</div>
);
};
export default Counter;
複製代碼
實現幾個經常使用的 自定義 Hook
這個其實 React 官網有說過,後期可能會成爲官方api,這裏只是簡單實現
import React, { useRef, useEffect, useState } from 'react';
function usePrevState<T>(state: T) {
const countRef = useRef<any>(null);
const [_state, setState] = useState<T>(state);
useEffect(() => {
countRef.current = _state;
setState(state);
}, [state]);
// prevState
return countRef.current;
}
export default usePrevState;
複製代碼
使用:
import React, { useState } from 'react';
import usePrevState from './usePrevState';
const Count2: React.FC = props => {
const [count, setCount] = useState<number>(0);
const prevCount = usePrevState<number>(count);
return (
<div>
<p>prevCount: {prevCount}</p>
<p>count: {count}</p>
<button type="button" onClick={() => setCount(prev => prev + 1)}>
increment
</button>
<button type="button" onClick={() => setCount(prev => prev - 1)}>
decrement
</button>
</div>
);
};
export default Count2;
複製代碼
這個是以前看一位大佬的 文章 05,裏面分享的另外一篇國外的 文章,而後本身根據實際使用改的
項目使用的是 UmiJS 框架,自帶的 request,
使用 axios 的話也是差很少的,把 fetchFn 類型改成
fetchFn: () => Promise<AxiosResponse>;
而後,請求函數改成 axios 相應的寫法就能夠了
說明:
import { useState, useEffect } from 'react';
import { RequestResponse } from 'umi-request';
export interface UseFetchDataProps {
fetchFn: () => Promise<RequestResponse>;
deps?: any[];
isReady?: boolean;
}
/** * 自定義 Hook: 獲取數據 * @param fetchFn {*} 使用 request 封裝的請求函數 * @param deps 更新依賴,從新執行 * @param isReady 能夠獲取數據標誌,默認直接獲取數據 */
export default function useFetchData({ fetchFn, deps = [], isReady }: UseFetchDataProps) {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isError, setIsError] = useState<boolean>(false);
const [resData, setResData] = useState<any>();
useEffect(() => {
let isDestroyed = false;
const getData = async () => {
try {
setIsLoading(true);
const res = await fetchFn();
if (!isDestroyed) {
setResData(res);
setIsLoading(false);
}
} catch (err) {
console.error(err);
setIsError(true);
}
};
// 默認(undefined)直接獲取數據
// 有條件時 isReady === true 再獲取
if (isReady === undefined || isReady) {
getData();
}
return () => {
isDestroyed = true;
};
}, deps);
return {
isLoading,
isError,
resData,
};
}
複製代碼
使用:
const { isLoading, resData } = useFetchData({
fetchFn: () => getAccountList(searchParams),
deps: [searchParams],
isReady: Boolean(searchParams.companyId),
});
// getAccountList 是這樣的:
export function getAccountList(params: AccountListRequestProps) {
return request('/accountList', {
params,
});
}
複製代碼
import { createModel } from 'hox';
import { useState, useEffect } from 'react';
import { getCompanyList } from '@/api';
const useCompanyModel = () => {
const [isLoading, setIsLoading] = useState(false);
const [companyList, setCompanyList] = useState([]);
useEffect(() => {
if (!companyList.length) getData();
}, [companyList]);
const getData = async () => {
setIsLoading(true);
const res = await getCompanyList({ userId: '11' });
setCompanyList(res);
setIsLoading(false);
};
return {
isLoading,
companyList,
};
};
export default createModel(useCompanyModel);
複製代碼
import React from 'react';
import useCompanyModel from '@/models/useCompanyModel';
export interface CompanyListProps {
onChange: (id: string) => void;
}
const CompanyList: React.FC<CompanyListProps> = ({ onChange }) => {
const { isLoading, companyList } = useCompanyModel();
//...
}
export default CompanyList;
複製代碼