status가 "C" : 체크박스가 checked된 것, checkedWordList에 단어가 들어가 있는 것
status가 "A" : 체크박스가 checked 해제된 것, checkedWordList에 단어가 없음
import {
loadWordsRequest,
loadCheckedRequest,
changeStatusWordAllRequest,
} from "../../redux/feature/wordSlice";
useEffect(() => {
dispatch(loadWordsRequest());
dispatch(loadCheckedRequest());
}, []);
const onChangeAllSelected = useCallback((e) => {
const checkboxClicked = e.target;
const userId = e.target.value;
if (checkboxClicked.checked) {
dispatch(changeStatusWordAllRequest({ status: "C", userId: userId }));
} else if (!checkboxClicked.checked) {
dispatch(changeStatusWordAllRequest({ status: "A", userId: userId }));
}
}, []);
return (
//전체 체크박스
<div className="flex">
<p className="w-1/2">
총 추가된 단어 :
<span className="font-bold text-red-500 ml-3">
{checkedWordList.length}
</span>
</p>
<div className="w-1/2 bg-gray-100">
<input
checked={showStatus > 0 ? true : false}
onChange={onChangeAllSelected}
value={UserId}
name="checkItem"
type="checkbox"
className="mr-1 h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
<span className="font-bold">
전체 선택 / 해제
</span>
</div>
</div>
//개별 단어 체크박스
<div className="group justify-center flex overflow-y-auto max-h-96 overflow-hidden rounded-md">
<div>
{wordLists.map((word, index) => { //단어 목록들
{
return (
<StartWordList word={word} index={index} />
);
}
})}
</div>
</div>
)
import React, { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { changeStatusWordRequest } from "../../redux/feature/wordSlice";
const StartWordList = ({ word, index }) => {
const dispatch = useDispatch();
const [bChecked, setBChecked] = useState(false);
const { checkedWordList } = useSelector((state) => state.word);
useEffect(() => {
if (word.status === "C") {
setBChecked(true);
} else if (word.status === "A") {
setBChecked(false);
}
}, [word.status]);
const onChangeSelected = useCallback((e) => {
const checkboxClicked = e.target;
const wordIndex = e.target.value;
if (checkboxClicked.checked) {
setBChecked(true);
dispatch(changeStatusWordRequest({ id: wordIndex, status: "C" }));
} else if (!checkboxClicked.checked) {
setBChecked(false);
dispatch(changeStatusWordRequest({ id: wordIndex, status: "A" }));
}
}, []);
return (
<div className="m-1 flex border-2 border-light-green h-12 w-80 rounded-md">
<input
checked={bChecked}
onChange={onChangeSelected}
value={word.id}
name="checkItem"
type="checkbox"
className="mt-3 ml-2 h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
<li className="my-2.5 flex">
<div className="flex ml-2">
<p className="text-sm font-medium text-slate-900 w-32 truncate">
({index}) {word.english} ({word.status})
</p>
<p className="ml-3 text-sm font-medium text-slate-900 w-32 truncate">
뜻: {word.korean}
</p>
</div>
</li>
</div>
);
};
export default StartWordList;
function loadWordsAPI() {
return axios.get("/words");
}
function* loadWords(action) {
try {
const data = action.payload;
const result = yield call(loadWordsAPI, data);
yield put(loadWordsSuccess(result.data));
} catch (error) {
yield put(loadWordsFailure(error));
console.log(error);
}
}
//loadChecked
function loadCheckedAPI() {
return axios.get("/words/checked");
}
function* loadChecked(action) {
try {
const data = action.payload;
const result = yield call(loadCheckedAPI, data);
yield put(loadCheckedSuccess(result.data));
} catch (error) {
yield put(loadCheckedFailure(error));
console.log(error);
}
}
loadWordsSuccess: (state, action) => {
const data = action.payload;
state.loadWordsLoading = false;
state.loadWordsComplete = true;
state.wordLists.length = 0;
state.wordLists = state.wordLists.concat(data);
},
loadCheckedSuccess: (state, action) => {
const data = action.payload;
state.loadCheckedLoading = false;
state.loadCheckedComplete = true;
state.checkedWordList.length = 0; //초기화
state.checkedWordList = state.checkedWordList.concat(data);
},
const express = require("express");
const { Op } = require("sequelize");
const { User, Word } = require("../models");
const router = express.Router();
//전체 단어들
router.get("/", async (req, res, next) => {
try {
const where = {};
if (parseInt(req.query.lastId, 10)) {
// 초기 로딩이 아닐 때
where.id = { [Op.lt]: parseInt(req.query.lastId, 10) };
} // 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
const words = await Word.findAll({
//모든 게시글 가져옴
where,
limit: 10,
order: [
["createdAt", "DESC"], //최신 게시글부터
],
include: [
{
model: User,
attributes: ["id", "nickname"],
},
],
});
console.log(words);
res.status(200).json(words);
} catch (error) {
console.error(error);
next(error);
}
});
router.get("/checked", async (req, res, next) => {
try {
const where = { status: "C", UserId: req.user.id };
if (parseInt(req.query.lastId, 10)) {
// 초기 로딩이 아닐 때
where.id = { [Op.lt]: parseInt(req.query.lastId, 10) };
} // 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
const words = await Word.findAll({
//모든 게시글 가져옴
where,
limit: 10,
order: [
["createdAt", "DESC"], //최신 게시글부터
],
});
console.log(words);
res.status(200).json(words);
} catch (error) {
console.error(error);
next(error);
}
});
module.exports = router;
function changeStatusAPI(data) {
return axios.patch(`/word/${data.id}/${data.status}`, {
id: data.id,
status: data.status,
});
}
function* changeStatus(action) {
try {
const data = action.payload;
const result = yield call(changeStatusAPI, data);
yield put(changeStatusWordSuccess(result.data));
} catch (error) {
yield put(changeStatusWordError(error));
console.log(error);
}
}
function changeStatusAllAPI(data) {
return axios.patch(`/word/all/${data.userId}/${data.status}`, {
status: data.status,
userId: data.userId,
});
}
function* changeStatusAll(action) {
try {
const data = action.payload;
const result = yield call(changeStatusAllAPI, data);
yield put(changeStatusWordAllSuccess(result.data));
} catch (error) {
yield put(changeStatusWordAllError(error));
console.log(error);
}
}
//개별 체크박스
changeStatusWordSuccess: (state, action) => {
const data = action.payload;
state.changeStatusWordLoading = false;
state.changeStatusWordComplete = true;
const changeStatus = state.wordLists.find((v) => v.id === data.id);
//이미 status가 "C"인 경우
const showStatus = state.wordLists.find(
(v) => v.id === data.id && v.status === "C"
);
if (changeStatus) {
changeStatus.status = data.status;
state.checkedWordList = state.checkedWordList.concat(data);
}
if (showStatus) {
//C -> A로 바꾸는 경우
const index = state.checkedWordList.findIndex(
(word) => word.id === data.id
);
state.checkedWordList.splice(index, 1); //인덱스 값 확인 후 제거
state.checkedWordList = state.checkedWordList.filter(
(word) => word.id !== data.id //제거된 값만 제외한 나머지
);
}
},
//전체 체크박스
changeStatusWordAllSuccess: (state, action) => {
const data = action.payload;
state.changeStatusWordLoading = false;
state.changeStatusWordComplete = true;
const changeStatus = state.wordLists.find((v) => v.UserId === data[0].UserId);
const showStatus = state.wordLists.find(
(v) => v.UserId === data[0].UserId && v.status === "C"
);
if (changeStatus) {
state.wordLists.map((word) => {
word.status = data[0].status;
state.checkedWordList.length = 0; //초기화
state.checkedWordList = state.checkedWordList.concat(data);
});
}
if (showStatus) {
state.checkedWordList.length = 0; //초기화
}
},
router.patch("/:id/:status", isLoggedIn, async (req, res, next) => {
// PATCH /word/10/status
try {
await Word.update(
{
status: req.params.status,
},
{
where: {
id: req.params.id,
UserId: req.user.id, //작성자가 본인이 맞는지?
},
}
);
const fullWord = await Word.findOne({
where: { id: req.params.id },
});
res.status(201).json(fullWord); //단어 정보 가져오기
} catch (error) {
console.error(error);
next(error);
}
});
router.patch("/all/:userId/:status", isLoggedIn, async (req, res, next) => {
// (전체 수정) PATCH /word/status/1(userId)
try {
await Word.update(
{
status: req.params.status,
},
{
where: {
[Op.or]: [{ id: { [Op.gt]: 0 } }, { id: req.params.userId }],
UserId: req.user.id,
},
}
);
const fullWord = await Word.findAll({
where: { status: req.params.status },
});
res.status(200).json(fullWord); //전체 단어 정보 가져오기
} catch (error) {
console.error(error);
next(error);
}
});