#reactjs #redux #xlsx #redux-saga
#reactjs #redux #xlsx #redux-saga
Вопрос:
Я новичок в redux-sagas и изо всех сил пытаюсь заставить приведенный ниже код работать.
Это сага, с которой я работаю, предполагается, что она считывает файл xlsx и отправляет действие по завершении. Действие должно быть отправлено ПОСЛЕ того, как файл будет полностью прочитан, но это не так, и я понятия не имею, что я делаю не так.
import { put } from 'redux-saga/effects';
import * as XLSX from 'xlsx';
import * as actionTypes from '../actions/actionTypes';
export function* importFileSaga(action) {
console.log('[importFileSaga]', action.fileToUpload);
const response = yield importFile(action.fileToUpload);
console.log('[importFileSaga]', response);
yield put ({type: actionTypes.SET_DATA, payload: response});
}
const importFile = file2upload => {
console.log('[importFile]', file2upload);
const reader = new FileReader();
reader.onload = (evt) => {
/* Parse data */
const bstr = evt.target.result;
const wb = XLSX.read(bstr, { type: 'binary' });
/* Get first worksheet */
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
const data = XLSX.utils.sheet_to_csv(ws, { header: 1 });
const processedData = processData(data);
console.log('[importFile]',processedData);
return processedData;
};
reader.readAsBinaryString(file2upload);
}
// process CSV data
const processData = dataString => {
const dataStringLines = dataString.split(/rn|n/);
const headers = dataStringLines[0].split(/,(?![^"]*"(?:(?:[^"]*"){2})*[^"]*$)/);
const list = [];
for (let i = 1; i < dataStringLines.length; i ) {
const row = dataStringLines[i].split(/,(?![^"]*"(?:(?:[^"]*"){2})*[^"]*$)/);
if (headers amp;amp; row.length === headers.length) {
const obj = {};
for (let j = 0; j < headers.length; j ) {
let d = row[j];
if (d.length > 0) {
if (d[0] === '"')
d = d.substring(1, d.length - 1);
if (d[d.length - 1] === '"')
d = d.substring(d.length - 2, 1);
}
if (headers[j]) {
obj[headers[j]] = d;
}
}
// remove the blank rows
if (Object.values(obj).filter(x => x).length > 0) {
list.push(obj);
}
}
}
// prepare columns list from headers
const columns = headers.map(c => ({
name: c,
selector: c,
}));
const processedData = {header: columns, data: list};
console.log('[processData]', processedData);
return processedData;
}
Когда загружается новый файл, это последовательность вывода, которую я получаю в консоли
[importFileSaga]
File { name: "test.csv", lastModified: 1603705847000, webkitRelativePath: "", size: 1580, type: "text/csv" }
importFile.js:7
[importFile]
File { name: "test.csv", lastModified: 1603705847000, webkitRelativePath: "", size: 1580, type: "text/csv" }
importFile.js:14
[importFileSaga] undefined importFile.js:9
SET_DATA reducers.js:31
undefined
TypeError: action.payload is undefined
The above error occurred in task importFileSaga
created by takeEvery(UPLOAD_FILE_START, importFileSaga)
created by watchImport
Tasks cancelled due to error:
takeEvery(UPLOAD_FILE_START, importFileSaga)
takeEvery(TEST_SAGA_INIT, importDeviceSaga) index.js:1
[processData]
Object { header: (50) […], data: (2) […] }
importFile.js:71
[importFile]
Object { header: (50) […], data: (2) […] }
Это неожиданно…но я предполагаю, что я все еще могу быть немного сбит с толку.
Я бы ожидал чего-то подобного
[importFileSaga]
File { name: "test.csv", lastModified: 1603705847000, webkitRelativePath: "", size: 1580, type: "text/csv" }
importFile.js:7
[importFile]
File { name: "test.csv", lastModified: 1603705847000, webkitRelativePath: "", size: 1580, type: "text/csv" }
importFile.js:14
[processData]
Object { header: (50) […], data: (2) […] }
importFile.js:71
[importFile]
Object { header: (50) […], data: (2) […] }
[importFileSaga] some data here importFile.js:9
SET_DATA reducers.js:31
Я понимаю, что эта строка кода
const ответ = выход importFile(action.fileToUpload);
означает, что строка ниже не выполняется, пока не будет завершен importFile. Это правильно? Как я могу это исправить?
— РЕДАКТИРОВАТЬ —
Благодаря markerikson я изменил функцию importFile
const importFile = file2upload => {
return new Promise((resolve, reject) => {
console.log('[importFile]', file2upload);
const reader = new FileReader();
reader.onload = (evt) => {
/* Parse data */
const bstr = evt.target.result;
const wb = XLSX.read(bstr, { type: 'binary' });
/* Get first worksheet */
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
const data = XLSX.utils.sheet_to_csv(ws, { header: 1 });
const processedData = processData(data);
console.log('[importFile]',processedData);
//return processedData;
resolve(processedData);
};
reader.readAsBinaryString(file2upload);
});
}
и теперь это работает как шарм!!
Ответ №1:
Концептуально здесь две проблемы:
yield importFile()
собирается перейтиundefined
к промежуточному программному обеспечению saga, которое, предположительно, будет продолжать работать- Программа чтения файлов не будет выполнять обратный вызов сразу, и вашему коду действительно нужно подождать, пока это не будет сделано.
Вам нужно будет реструктурировать код так, чтобы он ожидал, пока не будет выполнена логика чтения.
Одним из вариантов может быть ручное создание Promise
внутри importFile
и возврат его, а также разрешение обещания в конце .onload
обратного вызова.