오늘도 코딩하나
[moneybook]_day5 본문
1. (issue) 문자열 선택
초기값 세팅으로 0이 세팅된 후에도 selectText() 함수가 실행되어 문자열 전체 선택이 되야하는데, 안되넹
- 수정 후 소스
const setInitial = (e, item) => {
const today = new Date();
const date = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2,'0')}-${today.getDate()}`;
if(item.disabled) {
const newData = tempData.map((i) =>
item.id === i.id ? { ...i, date, price1:0, price2:0, isDisabled: false } : i
)
setTempData(newData);
setTimeout(() => selectText(e), 0);
} else {
selectText(e);
}
}
기존에는 selectText(e)를 그냥 호출했다.
[원인] React의 상태 업데이트와 브라우저의 DOM 갱신 타이밍 차이
(1) 상태 업데이트로 인한 비동기 재렌더링
- 상태 업데이트는 비동기적으로 처리되며, React는 변경된 상태를 기반으로 컴포넌트를 다시 렌더
- 이 재렌더링 과정이 끝나기 전에 selectText 함수가 호출될 수 있다.
(2) 브라우저의 DOM 갱신 타이밍
- 항목의 상태 변경에 대한 DOM 반영 전 selectText 호출
[해결 방법] SetTimeout
- setTimeout을 사용하여 selectText 호출을 지연시키면,
React가 상태 업데이트로 인해 컴포넌트를 다시 렌더링하고 DOM이 완전히 준비된 후에 텍스트 선택이 실행된다.
2. DB 연동
- server.js
const express = require('express');
const db = require('./database/db')
const app = express();
const path = require('path');
const port = 8009;
app.listen(port, function() {
console.log(`listening on ${port}`);
})
app.use(express.json());
var cors = require('cors');
app.use(cors());
app.use(express.static(path.join(__dirname, '../account/build')))
app.get('/payList', function(req, res) {
db.query('SELECT id, date, group1, group2, price1, price2, payment, remark FROM PAYLIST', function (err, results, fields) {
if(err) throw err;
res.send(results);
})
})
app.post('/payList/insert', function(req, res) {
console.log(req);
const data = req.body;
data.forEach(item => {
if(item.isNew) {
db.query('INSERT INTO PAYLIST (date, group1, group2, price1, price2, payment, remark) VALUES (?, ?, ?, ?, ?, ?, ?)',
[item.date, item.group1, item.group2, item.price1, item.price2, item.payment, item.remark],
(err, result) => {
if(err) throw err;
});
} else if(item.isModified) {
db.query('UPDATE PAYLIST SET date = ?, group1 = ?, group2 = ?, price1 = ?, price2 = ?, payment = ?, remark = ? WHERE id = ?',
[item.date, item.group1, item.group2, item.price1, item.price2, item.payment, item.remark, item.id],
(err, result) => {
if(err) throw err;
});
}
});
res.send({ message: 'Data saved successfully!'});
});
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, '../account/build', 'index.html'));
})
- paySlice.js
import { configureStore, createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from 'axios';
// 비동기 thunk : MySQL에서 데이터 가져오기
export const fetchData = createAsyncThunk('payList/fetchData', async () => {
const response = await axios.get('http://localhost:8009/payList');
return response.data;
});
// 비동기 thunk : MySQL에서 데이터 저장하기
export const saveData = createAsyncThunk('payList/saveData', async (data) => {
const response = await axios.post('http://localhost:8009/payList/insert', data);
return response.data;
})
let payList = createSlice({
name : 'payList',
initialState : {
items: [],
status: 'idle',
error: null,
},
extraReducers: (builder) => {
builder
.addCase(fetchData.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchData.fulfilled, (state, action) => {
state.status = 'succeeded';
state.items = action.payload;
})
.addCase(fetchData.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
})
.addCase(saveData.fulfilled, (state, action) => {
state.items = action.payload;
})
}
})
- PayList.js
function PayList() {
// 저장하기
const handleSave = () => {
const modifiedData = tempData.filter(item => item.isModified || item.isNew);
console.log(modifiedData);
if(modifiedData.length > 0) {
dispatch(saveData(tempData));
}
}
return (
{payListStatus === 'loading' && <div>Loading...</div>}
{payListStatus === 'succeeded' && (
tempData.map((item,i) => (
<tr key={i}>
<td><input type="checkbox" checked={checkedItems.includes(item.id)} onChange={() => handleCheck(item.id)} disabled={item.isDisabled} /></td>
{columns.map((col, idx) => (
<Input
key={col}
ref={el => inputRefs.current[i * 7 + idx] = el}
onBlur={(e) => handleUpdate(item.id, col, e.target.innerText)}
onKeyDown={(e) => handleKeyDown(e, (i + 1) * 7 + idx, e)}
onFocus={(e) => setInitial(item, i * 7 + idx)}>
{col.includes('price') && !isNaN(item[col]) ? Number(item[col]).toLocaleString('ko-KR') : item[col]}
</Input>
))}
</tr>
))
)}
{payListStatus === 'failed' && (
<tr>
<td colspan="8">Error</td>
</tr>
)}
)
}
React + Redux + MySQL
DB에서 데이터를 불러오고, 새로운 데이터를 DB에 저장하는 것까지 완료했다!
** DB 세팅에 대한 내용은 하단 링크에 정리해둠
https://coding-hana.tistory.com/49
'React > Daily' 카테고리의 다른 글
[moneybook]_day6 (0) | 2024.09.02 |
---|---|
[moneybook]_day4 (0) | 2024.08.29 |
[moneybook]_day3 (0) | 2024.08.24 |
[moneybook]_day2 (0) | 2024.08.24 |
[moneybook]_day1 (0) | 2024.08.22 |