사용자의 uid와 할 일이 생성된 날짜를 기준으로 생성된 투두리스트를 불러오고, 할 일을 추가하는 기능을 구현하려 한다.
AddButton
을 클릭하면 modalIsOpen
상태가 토글되며, AddTodo
컴포넌트가 조건부로 렌더링 되게 함modalIsOpen
상태가 true이면 모달창이 렌더링function MainPage() {
const [currentUser, setcurrentUser] = useState();
const [modalIsOpen, setModalIsOpen] = useState(false);
const user = useSelector(state=>state.User.userData);
if(currentUser !== undefined){
return (
<m.MainpageLayout>
{...생략...}
<m.AddButton onClick={()=>setModalIsOpen(!modalIsOpen)}>
<BsPlusCircleDotted className='add_icon'/>
<p className='add_content'>새로운 할 일 생성하기</p>
</m.AddButton>
{modalIsOpen && <AddTodo onClose={setModalIsOpen} />}
</m.MainpageLayout>
)
}else{
<div>
로딩중...
</div>
}
}
export default auth(MainPage, true);
import React,{useState} from 'react'
import * as a from '../../../../style/AddTodostyle'
import {FaMicrophone} from 'react-icons/fa'
import {ImCancelCircle} from 'react-icons/im'
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux'
import { addTodo } from '../../../../_redux/todo';
import dayjs from 'dayjs';
function AddTodo(props) {
const user = useSelector(state=>state.User.userData);
const {onClose} = props;
const [Todo, setTodo] = useState();
const dispatch = useDispatch();
const handleCloseModal = () => {
setTodo('');
onClose(false);
};
const handleChange = (e)=>{
setTodo(e.target.value);
}
const handleSubmit = ()=>{
let variable = {
Time: dayjs().format('YYYY-MM-DD'),
Content: Todo,
Uid: user.Uid,
Completed: false
}
dispatch(addTodo(variable));
handleCloseModal();
}
return (
<a.ModalLayout>
<div style={{ width: '95%', display: 'flex', flexDirection: 'row', alignItems: 'center'}}>
<p className='modaltitle'>할 일 추가하기</p>
<ImCancelCircle onClick={handleCloseModal} style={{width: '1.8rem', height: '1.8rem', marginLeft: '12rem', color:'#1B1D1F'}}/>
</div>
<hr/>
<a.ContentInput type="text" value={Todo} onChange={handleChange}>
</a.ContentInput>
<FaMicrophone className='microphone'/>
<a.SubmitBtn onClick={handleSubmit}>등록하기</a.SubmitBtn>
</a.ModalLayout>
)
}
export default AddTodo
1. handleSubmit
함수
variable
객체 생성: 새로운 할 일 데이터를 나타내는 variable
객체를 생성 Time
: 현재 날짜를 dayjs
를 사용하여 "YYYY-MM-DD" 형식으로 포맷Content
: 사용자가 입력한 할 일 내용(Todo
state)Uid
: 현재 사용자의 UID(user.Uid
)Completed
: 할 일의 완료 상태(기본적으로 false
로 설정)handleCloseModal
함수를 실행import { saveTodo, findallTodo } from "../firebase/firebase_todo";
//Actions
const ADD_TODO = 'todo_reducer/addtodo';
// Action Creators
export function addTodo(data){
saveTodo(data);
return {
type: ADD_TODO,
payload: data
};
};
//Reducer
const initialState = {
todos:[]
}
export default function (state = initialState, action){
switch (action.type) {
case ADD_TODO:
// 현재 todos 배열에 새로운 todo 데이터 추가
const newTodos = [...state.todos, action.payload];
return { ...state, todos: newTodos };
default:
return state;
}
}
ADD_TODO
: 할 일을 추가하는 액션 타입을 상수로 정의addTodo(data)
: 새로운 할 일 데이터를 saveTodo함수를 통해 Firebase에 저장한 다음, 이 데이터를 Redux 스토어에 추가하는 액션을 생성. 생성된 액션 객체에는 type
과 payload
가 구성되며, payload
에는 추가된 할 일 데이터가 포함된다.ADD_TODO
액션 타입에 대한 처리: 현재 todos
배열에 새로운 할 일 데이터(action.payload
)를 추가한 새로운 배열을 생성하고, 이 배열을 포함하는 새로운 상태 객체를 반환export async function saveTodo(data){
const todos = firestore.collection("todos");
try{
await todos.doc().set(data)
}catch(error){
console.log(error);
throw error;
}
}
function MainPage() {
const [currentUser, setcurrentUser] = useState();
const user = useSelector(state=>state.User.userData);
useEffect(()=>{
if(user && Object.keys(user).length > 0){
console.log(user);
setcurrentUser(user);
}else{
console.log("No user")
}
if(currentUser){
console.log(selectedDay);
dispatch(initializeTodo({
uid: currentUser.Uid,
time: selectedDay
}))
}
}, [user]);
}
if (user && Object.keys(user).length > 0)
조건문을 사용해, 사용자 데이터(user
)가 비어있지 않고, 적어도 하나 이상의 속성을 가지고 있다면 currentUser state에 해당 user정보를 넣어주었다.import { saveTodo, findallTodo } from "../firebase/firebase_todo";
//Actions
const INITIALIZE_TODOS = 'todo_reducer/initializetodo'
// Action Creators
export async function initializeTodo(data){
try{
const todos = await findallTodo(data);
console.log(todos);
return{
type: INITIALIZE_TODOS,
payload: todos
}
}catch(error){
console.error("Todo 데이터 초기화 실패:", error);
throw error;
}
}
//Reducer
const initialState = {
todos:[]
}
export default function (state = initialState, action){
switch (action.type) {
case INITIALIZE_TODOS:
return {...state, todos: action.payload}
default:
return state;
}
}
INITIALIZE_TODOS
: 할 일 목록을 초기화하는 액션 타입을 상수로 정의initializeTodo(data)
: 특정 사용자 및 날짜에 해당하는 할 일 목록을 Firebase에서 검색하고, 검색된 할 일 목록을 Redux 스토어에 초기화하는 비동기 액션을 생성. 이 액션은 findallTodo
함수를 사용하여 데이터를 가져와서 가져온 데이터를 payload
로 설정하여 리턴INITIALIZE_TODOS
액션 타입에 대한 처리: 액션의 payload
로 받아온 할 일 목록 데이터를 todos
배열로 설정한 새로운 상태 객체를 반환findallTodo(data)
: Firebase Firestore를 사용하여 특정 사용자 및 시간에 해당하는 할 일 목록 데이터를 검색하는 함수export async function findallTodo(data){
const todos = firestore.collection("todos");
//firestore 객체를 사용하여 Firestore의 'todo'컬렉션을 가져옴
const result = [] // 데이터를 저장할 배열
try{
//todos 컬렉션에서 Uid 필드가 data.uid와 일치하고,
//Time 필드가 data.time과 일치하는 문서들을 검색하기 위한 Firestore 쿼리를 생성
const query = todos.where('Uid', '==', data.uid).where('Time', '==', data.time);
const snapshot = await query.get(); //쿼리 실행
if(!snapshot.empty){
snapshot.forEach((doc)=>{
const data = doc.data(); //각 문서의 데이터를 doc.data()를 사용하여 가져
console.log(data);
result.push(data); //할일 데이터를 result 배열에 추가
})
return result; //배열 리턴
}else{
return [];
}
}catch(error){
console.log(error);
throw error;
}
}