이전 - (17) redux로 File Upload 상태 관리하기
현재 - (18) File Upload에 대한 이벤트 보완
이전 글에서 store에 FileUploadFlag를 관리할 수 있게 되었다.
이제 아래의 문제를 보완해보자.
1) file을 불러와도 drag & drop 영역이 사라지지 않는다.
2) DOWNLOAD 버튼/displayCell은 file이 업로드 된 후에 필요하다. (즉, MyTable은 file이 업로드 된 후 보인다.)
→ fileUpload가 성공하면 flag를 true로 변경한다.
flag가 on이면 FileUpload의 drag & drop 영역은 사라지게 한다.
그리고 MyTable이 나타나게 한다.
먼저 이전에 App.js의 주석을 풀어주자.
const App = () => {
const [csvObject, setCsvObject] = useState(csvObjectDefault);
return (
<div>
{/* <ReduxTest/>
<AnotherReduxTest/> */}
<button onClick={() => console.log(csvObject)}>print csv</button>
<div className="App">
<MyTable csvFile={csvObject}/>
<FileUpload setCsvObject={setCsvObject} />
</div>
</div>
);
};
FileUpload에 store를 연결한다.
flagOff는 필요없지만 이전 예제처럼 그대로 추가한다.
flagOn을 handleOnDrop과 handleUpload에 넘겨줘서 실행시킨다.
fileUploadFlag가 off가 되면 drag & drop 영역을 없애면 되므로,
아래와 같이 { !fileUploadFlag && drag & drop 영역 } 으로 간단히 해결한다.
// FileUpload.js
import React, { useState } from "react";
import { connect } from "react-redux";
import { actionCreators } from "./store";
...
const FileUpload = ({ setCsvObject, fileUploadFlag, flagOn, flagOff }) => {
const [dropFlag, setDropFlag] = useState(false);
return (
<div>
{!fileUploadFlag && (
<div
onDrop={(e) => lib.handleOnDrop(e, setDropFlag, setCsvObject, flagOn)}
>
<input
onChange={(e) => lib.handleUpload(e, setCsvObject, flagOn)}
/>
</div>
)}
</div>
);
};
flagOn를 drop/upload에 넘겨주었으므로, library.js도 아래와 같이 변경한다.
// library.js
...
export const handleOnDrop = (e, setState, setCsvObject, flagOff) => {
e.preventDefault();
...
setState(false);
flagOff();
return false;
};
export const handleUpload = (e, setCsvObject, flagOff) => {
...
fileReader.onload = function () {
//console.log(fileReader.result);
parsingCsv(fileReader.result, setCsvObject);
};
flagOff();
}
MyTable에서는 flag를 on/off 할 필요 없이, flag의 변경에 대해 반응만 하면 된다.
즉, Upload 성공 시 flag on에 의해 MyTable이 나타나게 하면 된다.
FileUpload와는 반대로 { fileUploadFlag && MyTable 영역 } 이 된다.
//MyTable.js
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
...
const MyTable = ({ csvFile, fileUploadFlag }) => {
...
return (
<div>
{fileUploadFlag && (
<div>
<button onClick={csvDownLoad}>DOWNLOAD</button>
<div>
<span>{displayIndex}</span>
<input value={displayCell} onChange={setValueCell} />
</div>
<div id="hot-app"></div>
</div>
)}
</div>
);
};
function mapStateToProps(state, ownProps) {
//console.log(state);
return { fileUploadFlag: state };
}
export default connect(mapStateToProps)(MyTable);
파일 업로드 전에는 drag & drop 영역만 있다가,
file upload(drop, 파일 선택)가 완료된 후에는 Table이 나타나는 것을 알 수 있다.
최종 코드는 아래와 같다.
//App.js
import React, { useState } from "react";
//import * as lib from "./components/library";
import FileUpload from "./components/FileUpload";
import MyTable from "./components/MyTable";
//import ReduxTest from "./components/ReduxTest";
//import AnotherReduxTest from "./components/AnotherReduxTest";
const csvObjectDefault = {
HEIGHT: 0,
WIDTH: 0,
csv: [],
};
const App = () => {
const [csvObject, setCsvObject] = useState(csvObjectDefault);
return (
<div>
{/* <ReduxTest/>
<AnotherReduxTest/> */}
<button onClick={() => console.log(csvObject)}>print csv</button>
<div className="App">
<MyTable csvFile={csvObject}/>
<FileUpload setCsvObject={setCsvObject} />
</div>
</div>
);
};
export default App;
// FileUpload.js
import React, { useState } from "react";
import { connect } from "react-redux";
import { actionCreators } from "./store";
import * as lib from "./library.js";
import "../css/FileUpload.scss";
const FileUpload = ({ setCsvObject, fileUploadFlag, flagOn, flagOff }) => {
const [dropFlag, setDropFlag] = useState(false);
return (
<div>
{!fileUploadFlag && (
<div
id="drag-drop-field"
className={dropFlag ? "in" : ""}
onDrop={(e) => lib.handleOnDrop(e, setDropFlag, setCsvObject, flagOn)}
onDragOver={(e) => lib.handleDragOver(e, setDropFlag)}
onDragLeave={(e) => lib.handleOnDragLeave(e, setDropFlag)}
>
<p>drag & drop</p>
<input
type="file"
accept=".csv"
onChange={(e) => lib.handleUpload(e, setCsvObject, flagOn)}
/>
</div>
)}
</div>
);
};
function mapStateToProps(state, ownProps) {
//console.log(state);
return { fileUploadFlag: state };
}
function mapDispatchToProps(dispatch, ownProps) {
return {
flagOn: () => dispatch(actionCreators.flagOn()),
flagOff: () => dispatch(actionCreators.flagOff()),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(FileUpload);
// library.js
...
export const handleOnDrop = (e, setState, setCsvObject, flagOff) => {
e.preventDefault();
...
setState(false);
flagOff();
return false;
};
export const handleUpload = (e, setCsvObject, flagOff) => {
...
fileReader.onload = function () {
//console.log(fileReader.result);
parsingCsv(fileReader.result, setCsvObject);
};
flagOff();
}
//MyTable.js
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import * as lib from "./library.js";
import "handsontable/dist/handsontable.full.css";
import Handsontable from "handsontable";
let myTable;
let currentRow, currentColumn;
const getCell = (cell) => {
if(cell === null) return ``;
return cell.includes(",") ? `"${cell}"` : `${cell}`;
}
const csvDownLoad = () => {
let rows = myTable.countRows();
let cols = myTable.countCols();
let tmpTables = myTable.getData(0, 0, rows - 1, cols - 1);
let maxRow, maxCol;
maxCol = 0;
for(let r = 0; r < rows; r++) {
for(let c = cols - 1; c >=0; c--) {
if(!(tmpTables[r][c] === "" || tmpTables[r][c] === null)) {
maxCol = (maxCol < c) ? c : maxCol;
break;
}
}
}
maxRow = 0;
for(let c = 0; c < cols; c++) {
for(let r = rows - 1; r >=0; r--) {
if(!(tmpTables[r][c] === "" || tmpTables[r][c] === null)) {
maxRow = (maxRow < r) ? r : maxRow;
break;
}
}
}
let parsing = myTable.getData(0, 0, maxRow, maxCol)
.map((item) => item.map((cell) => getCell(cell)));
let realTable = parsing.map((item) => item.join(",")).join("\n");
lib.downLoadCsv(realTable);
return;
};
const MyTable = ({ csvFile, fileUploadFlag }) => {
const [displayIndex, setDisplayIndex] = useState("");
const [displayCell, setDisplayCell] = useState("");
const selectCell = () => {
let selected = myTable.getSelectedLast();
currentRow = selected[0];
currentColumn = selected[1];
if(currentRow < 0 || currentColumn < 0) return;
setDisplayCell(myTable.getValue());
setDisplayIndex(`${lib.rowToAlpha(currentColumn + 1)}${currentRow + 1}`);
}
const setValueCell = (e) => {
if(currentRow < 0 || currentColumn < 0) return;
setDisplayCell(e.target.value);
myTable.setDataAtCell(currentRow, currentColumn, e.target.value);
}
const init = (csvFile) => {
if (csvFile === undefined || csvFile.HEIGHT === 0) return;
const container = document.getElementById("hot-app");
myTable = new Handsontable(container, {
data: lib.makeTable(csvFile, 2, 3),
colHeaders: true, /* column header는 보이게 설정 */
rowHeaders: true, /* row header 보이게 설정 */
colWidths: [60, 60, 60, 60, 60, 60, 60],
wordWrap: false, /* 줄 바꿈 x */
width: "50%",
manualColumnResize: true, /* column 사이즈 조절 */
manualRowResize: true, /* row 사이즈 조절 */
manualColumnMove: true, /* column move 허용 */
manualRowMove: true, /* row move 허용 */
dropdownMenu: true, /* dropdown 메뉴 설정 */
filters: true, /* 필터 기능 on */
contextMenu: true, /* cell 클릭 시 메뉴 설정 */
licenseKey: "non-commercial-and-evaluation",
afterSelection: selectCell,
});
};
useEffect(() => {
init(csvFile);
}, [csvFile]);
return (
<div>
{fileUploadFlag && (
<div>
<button onClick={csvDownLoad}>DOWNLOAD</button>
<div>
<span>{displayIndex}</span>
<input value={displayCell} onChange={setValueCell} />
</div>
<div id="hot-app"></div>
</div>
)}
</div>
);
};
function mapStateToProps(state, ownProps) {
//console.log(state);
return { fileUploadFlag: state };
}
export default connect(mapStateToProps)(MyTable);
'개발 > React' 카테고리의 다른 글
리액트 같은 이름의 파일 재업로드 (같은 파일 다시 열기) (0) | 2021.12.03 |
---|---|
React Handsontable로 csv 편집기 만들기 (19) (5) | 2021.06.29 |
React Handsontable로 csv 편집기 만들기 (17) (0) | 2021.06.26 |
React Handsontable로 csv 편집기 만들기 (16) (0) | 2021.06.14 |
React Handsontable로 csv 편집기 만들기 (15) (0) | 2021.06.13 |
댓글