vincentlu
2025-02-05 9d79b8c8d34077ebf782a7b61d54ee4e1debdba1
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import * as Papa from 'papaparse';
import { useCallback, useMemo, useRef, useState } from 'react';
 
export function usePapaParse({ batchSize = 10, processBatch }) {
    const importIdRef = useRef(0);
 
    const [importer, setImporter] = useState({
        state: 'idle',
    });
 
    const reset = useCallback(() => {
        setImporter({
            state: 'idle',
        });
        importIdRef.current += 1;
    }, []);
 
    const parseCsv = useCallback((file) => {
        setImporter({
            state: 'parsing',
        });
 
        const importId = importIdRef.current;
        Papa.parse(file, {
            header: true,
            skipEmptyLines: true,
            async complete(results) {
                if (importIdRef.current !== importId) {
                    return;
                }
 
                setImporter({
                    state: 'running',
                    rowCount: results.data.length,
                    errorCount: results.errors.length,
                    importCount: 0,
                    remainingTime: null,
                });
 
                let totalTime = 0;
                for (let i = 0; i < results.data.length; i += batchSize) {
                    if (importIdRef.current !== importId) {
                        return;
                    }
 
                    const batch = results.data.slice(i, i + batchSize);
                    try {
                        const start = Date.now();
                        await processBatch(batch);
                        totalTime += Date.now() - start;
 
                        const meanTime = totalTime / (i + batch.length);
                        setImporter(previous => {
                            if (previous.state === 'running') {
                                const importCount = previous.importCount + batch.length;
                                return {
                                    ...previous,
                                    importCount,
                                    remainingTime: meanTime * (results.data.length - importCount),
                                };
                            }
                            return previous;
                        });
                    } catch (error) {
                        console.error('Failed to import batch', error);
                        setImporter(previous =>
                            previous.state === 'running'
                                ? {
                                    ...previous,
                                    errorCount: previous.errorCount + batch.length,
                                }
                                : previous
                        );
                    }
                }
 
                setImporter(previous =>
                    previous.state === 'running'
                        ? {
                            ...previous,
                            state: 'complete',
                            remainingTime: null,
                        }
                        : previous
                );
            },
            error(error) {
                setImporter({
                    state: 'error',
                    error,
                });
            },
            dynamicTyping: true,
        });
    }, [batchSize, processBatch]);
 
    return useMemo(() => ({ importer, parseCsv, reset, }), [importer, parseCsv, reset]);
}