В этой теме несколько сообщений, но ни одна из них, похоже, не решает мою проблему.Я пытался использовать несколько разных библиотек, даже комбинации библиотек, чтобы получить желаемые результаты.Пока что мне не повезло, но я чувствую себя очень близко к решению.
По сути, я хочу скачать CSV-файл одним нажатием кнопки.Я использую компоненты Material-UI для кнопки и хотел бы сохранить функциональность как можно более тесно связанной с React, используя только vanilla JS, если это абсолютно необходимо.
Чтобы предоставить немного больше информации о конкретной проблеме,У меня есть список опросов.В каждом опросе есть определенное количество вопросов, и на каждый вопрос есть 2-5 ответов.После того, как разные пользователи ответили на вопросы, администратор веб-сайта сможет нажать кнопку, которая загрузит отчет.Этот отчет представляет собой файл CSV с заголовками, которые относятся к каждому вопросу, и соответствующими номерами, которые показывают, сколько людей выбрали каждый ответ.
Страница загрузкиКнопки CSV отображаются в списке.В списке отображаются заголовки и информация о каждом опросе.Таким образом, у каждого опроса в строке есть своя собственная кнопка загрузки.
Каждый опрос имеет уникальный идентификатор, связанный с ним.Этот идентификатор используется для извлечения в бэкэнд-сервис и извлечения соответствующих данных (только для этого опроса), которые затем преобразуются в соответствующий формат CSV.Поскольку в списке могут содержаться сотни опросов, данные следует выбирать только при каждом отдельном щелчке по кнопке соответствующего опроса.
Я попытался использовать несколько библиотек, таких как CSVLink и json2csv.Моей первой попыткой было использование CSVLink.По сути, CSVLink был скрыт и встроен в кнопку.При нажатии на кнопку запускается выборка, которая извлекает необходимые данные.Затем состояние компонента было обновлено и загружен файл CSV.
import React from 'react';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';
import { CSVLink } from 'react-csv';
import { getMockReport } from '../../../mocks/mockReport';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
color: '#FFF !important',
},
});
class SurveyResults extends React.Component {
constructor(props) {
super(props);
this.state = { data: [] };
this.getSurveyReport = this.getSurveyReport.bind(this);
}
// Tried to check for state update in order to force re-render
shouldComponentUpdate(nextProps, nextState) {
return !(
(nextProps.surveyId === this.props.surveyId) &&
(nextState.data === this.state.data)
);
}
getSurveyReport(surveyId) {
// this is a mock, but getMockReport will essentially be making a fetch
const reportData = getMockReport(surveyId);
this.setState({ data: reportData });
}
render() {
return (<CSVLink
style={{ textDecoration: 'none' }}
data={this.state.data}
// I also tried adding the onClick event on the link itself
filename={'my-file.csv'}
target="_blank"
>
<Button
className={this.props.classes.button}
color="primary"
onClick={() => this.getSurveyReport(this.props.surveyId)}
size={'small'}
variant="raised"
>
Download Results
</Button>
</CSVLink>);
}
}
export default withStyles(styles)(SurveyResults);
Проблема, с которой я продолжал сталкиваться, заключается в том, что состояние не будет обновляться должным образом до второго нажатия кнопки.Еще хуже, когда this.state.data передавался в CSVLink как реквизит, это всегда был пустой массив.В загруженном CSV данные не отображаются.В конце концов, казалось, что это не самый лучший подход.Мне все равно не понравилась идея иметь скрытый компонент для каждой кнопки.
Я пытался заставить его работать, используя компонент CSVDownload.(и CSVLink находятся в этом пакете: https://www.npmjs.com/package/react-csv)
Компонент DownloadReport отображает кнопку Material-UI и обрабатывает событие.Когда кнопка нажата, она распространяет событие на несколько уровней вплоть до компонента с состоянием и изменяет состояние allowDownload.Это, в свою очередь, запускает рендеринг компонента CSVDownload, который делает выборку для получения указанных данных опроса и приводит к загрузке CSV.
import React from 'react';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';
import DownloadCSV from 'Components/ListView/SurveyTable/DownloadCSV';
import { getMockReport } from '../../../mocks/mockReport';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
color: '#FFF !important',
},
});
const getReportData = (surveyId) => {
const reportData = getMockReport(surveyId);
return reportData;
};
const DownloadReport = props => (
<div>
<Button
className={props.classes.button}
color="primary"
// downloadReport is defined in a stateful component several levels up
// on click of the button, the state of allowDownload is changed from false to true
// the state update in the higher component results in a re-render and the prop is passed down
// which makes the below If condition true and renders DownloadCSV
onClick={props.downloadReport}
size={'small'}
variant="raised"
>
Download Results
</Button>
<If condition={props.allowDownload}><DownloadCSV reportData={getReportData(this.props.surveyId)} target="_blank" /></If>
</div>);
export default withStyles(styles)(DownloadReport);
Отображение CSVDownload здесь:
import React from 'react';
import { CSVDownload } from 'react-csv';
// I also attempted to make this a stateful component
// then performed a fetch to get the survey data based on this.props.surveyId
const DownloadCSV = props => (
<CSVDownload
headers={props.reportData.headers}
data={props.reportData.data}
target="_blank"
// no way to specify the name of the file
/>);
export default DownloadCSV;
Проблема здесь в том, что имя файла CSV не может быть указано.Кроме того, кажется, что каждый раз надежно не загружать файл.На самом деле, кажется, что это происходит только с первого клика.Похоже, что данные тоже не загружаются.
Я рассмотрел вариант использования пакетов json2csv и js-file-download, но я надеялся избежать использования vanilla JS и придерживаться только React.Это нормально, что беспокоиться?Также кажется, что один из этих двух подходов должен работать.Кто-нибудь уже сталкивался с подобной проблемой и имеет четкое предложение о том, как ее решить?
Я ценю любую помощь.Спасибо!