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;
复制代码