# 图片加载组件
使用hook来优雅编写一个图片加载组件。
# hook部分
import * as React from 'react';
function imgPromise(src: string): Promise<void> {
return new Promise((resolve, reject) => {
const i = new Image();
i.onload = () => resolve();
i.onerror = reject;
i.src = src;
});
}
function promiseFind(
sourceList: string[],
imgPromise: (src: string) => Promise<void>
): Promise<string> {
let done = false;
return new Promise((resolve, reject) => {
const queueNext = (src: string) => {
return imgPromise(src).then(() => {
done = true;
resolve(src);
});
};
const firstPromise = queueNext(sourceList.shift() || '');
sourceList
.reduce((p, src) => {
return p.catch(() => {
if (!done) return queueNext(src);
return;
});
}, firstPromise)
.catch(reject);
});
}
const removeBlankArrayElements = (a: string[]) => a.filter(x => x);
const stringToArray = (x: string | string[]) => (Array.isArray(x) ? x : [x]);
const cache: {
[key: string]: Promise<string>;
} = {};
export interface useImageParams {
loadImg?: (src: string) => Promise<void>;
srcList: string | string[];
}
function useImage({
loadImg = imgPromise,
srcList,
}: useImageParams): { src: string | undefined; loading: boolean; error: any } {
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);
const [value, setValue] = React.useState<string | undefined>(undefined);
const sourceList = removeBlankArrayElements(stringToArray(srcList));
const sourceKey = sourceList.join('');
React.useEffect(() => {
if (!cache[sourceKey]) {
cache[sourceKey] = promiseFind(sourceList, loadImg);
}
cache[sourceKey]
.then(src => {
setLoading(false);
setValue(src);
})
.catch(error => {
setLoading(false);
setError(error);
});
}, [sourceKey]);
return { loading: loading, src: value, error: error };
}
export default useImage;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 图片组件
import React from 'react';
import useImage, { useImageParams } from './useImage';
export type ImgProps = Omit<
React.DetailedHTMLProps<
React.ImgHTMLAttributes<HTMLImageElement>,
HTMLImageElement
>,
'src'
> &
Omit<useImageParams, 'srcList'> & {
src: useImageParams['srcList'];
loader?: JSX.Element | null;
unloader?: JSX.Element | null;
};
export default function Img({
src: srcList,
loadImg,
loader = null,
unloader = null,
...imgProps
}: ImgProps) {
const { src, loading, error } = useImage({
srcList,
loadImg,
});
if (src) return <img src={src} {...imgProps} />;
if (loading) return loader;
if (error) return unloader;
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34