Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- React
- 차집합
- useRef()
- 소수점 올림내림
- reat if문
- new Date()
- Number()
- Eventlitener
- setDate
- filter()
- Math.sqrt()
- parseInt()
- useState()
- jsx반복문
- getday()
- indexOf()
- Math.floor()
- map()
- substring()
- 교집합
- toUpperCase()
- useEffect()
- slice()
- charAt()
- repeat()
- includes()
- sort()
- isNaN()
- 항해99솔직후기 #항해99장점 #항해99단점 #부트캠프추천
- 3진수
Archives
- Today
- Total
개발자로 전향중
리액트 3주차 버킷리스트 만들기 연습 2022-01-26 본문
db에 연결!
My bucketlist!
설치 프로그램
App.js
import { Route, Routes } from "react-router-dom";
import React from "react";
// BucketList 컴포넌트를 import 해옵니다.
// import [컴포넌트 명] from [컴포넌트가 있는 파일경로];
import BucketList from "./BucketList";
import styled from "styled-components";
import Detail from "./detail";
import {useDispatch} from "react-redux";
import {createBucket, loadBucketFB, addBucketFB} from "./redux/modules/bucket"
import Progress from "./Progress";
// import {db} from "./firebase"
function App() {
const text = React.useRef(null);
const dispatch = useDispatch();
React.useEffect(() => {
dispatch(loadBucketFB());
}, [])
const addBucketList = () => {
dispatch(addBucketFB({text: text.current.value, completed: false}));
// dispatch(createBucket({text: text.current.value, completed: false}));
};
return (
<div className="App">
<Container>
<Title>내 버킷리스트</Title>
<Progress/>
<Line />
{/* 컴포넌트를 넣어줍니다. */}
{/* <컴포넌트 명 [props 명]={넘겨줄 것(리스트, 문자열, 숫자, ...)}/> */}
<Routes>
<Route path="/" element={<BucketList />} />
<Route path="/detail:index" element={<Detail />} />
</Routes>
</Container>
{/* 인풋박스와 추가하기 버튼을 넣어줬어요. */}
<Input>
<input type="text" ref={text} />
<button onClick={addBucketList}>추가하기</button>
</Input>
<button onClick={() => {
window.scrollTo({top:0, left:0, behavior: "smooth"});
}}>위로 가기</button>
</div>
);
}
const Input = styled.div`
max-width: 350px;
min-height: 10vh;
background-color: #fff;
padding: 16px;
margin: 20px auto;
border-radius: 5px;
border: 1px solid #ddd;
display: flex;
& > * {
padding : 5px;
}
& input{
border: 1px solid #888;
width: 70%;
margin-right: 10px;
}
& input:focus {
border: 1px solid #a673ff;
outline: none;
}
& button {
width: 25%;
color: #fff;
border: #a673ff;
background: #a673ff;
}
`;
const Container = styled.div`
max-width: 350px;
min-height: 60vh;
background-color: #fff;
padding: 16px;
margin: 20px auto;
border-radius: 5px;
border: 1px solid #ddd;
`;
const Title = styled.h1`
color: slateblue;
text-align: center;
`;
const Line = styled.hr`
margin: 16px 0px;
border: 1px dotted #ddd;
`;
export default App;
BucketList.js
import React from "react";
import styled from "styled-components";
import { useNavigate } from "react-router-dom";
import {useSelector} from "react-redux"
const BucketList = (props) => {
let navigate = useNavigate();
// const my_lists = props.list;
const my_lists = useSelector((state) => state.bucket.list);
return (
<ListStyle>
{my_lists.map((list, index) => {
return (
<ItemStyle
completed={list.completed}
className="list_item"
key={index}
onClick={() => {
navigate("/detail"+index);
}}
>
{list.text}
</ItemStyle>
);
})}
</ListStyle>
);
};
const ListStyle = styled.div`
display: flex;
flex-direction: column;
height: 50vh;
overflow-x: hidden;
overflow-y: auto;
max-height: 50vh;
`;
const ItemStyle = styled.div`
padding: 16px;
margin: 8px;
color: ${(props) => props.completed? "#fff": "#333"};
background-color: ${(props) => (props.completed ? "#673ab7" : "aliceblue")};
`;
export default BucketList;
detail.js
import React from "react";
import { useNavigate } from "react-router-dom";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux"
// 데이터가져올때
import { useDispatch } from "react-redux";
import { deleteBucket, updateBucket, updateBucketFB, deleteBucketFB} from "./redux/modules/bucket";
const Detail = (props) => {
const dispatch = useDispatch();
let navigate = useNavigate();
const params = useParams();
const bucket_index = params.index;
const bucket_list = useSelector((state) => state.bucket.list);
return (
<div>
<h1 onClick={() => {
navigate("/")
}}>{bucket_list[bucket_index] ? bucket_list[bucket_index].text : ""}</h1>
<button onClick={() => {
// dispatch(updateBucket(bucket_index));
dispatch(updateBucketFB(bucket_list[bucket_index].id))
navigate("/")
}}>완료하기</button>
<button onClick={() => {
// dispatch(deleteBucket(bucket_index));
dispatch(deleteBucketFB(bucket_list[bucket_index].id));
navigate("/")
}}>삭제하기</button>
</div>
)
};
export default Detail;
projess.js
import React from "react";
import styled from "styled-components";
import { useSelector } from "react-redux";
const Progress = () => {
const bucket_list = useSelector((state) => state.bucket.list)
let count = 0;
bucket_list.map((b, idx) => {
if (b.completed) {
count++;
}
})
return (
<ProgressBar>
<HighLight width={(count/bucket_list.length) * 100 + "%"} />
<Dot/>
</ProgressBar>
)
}
const ProgressBar = styled.div`
background: #eee;
width: 100%;
height: 20px;
display: flex;
align-items: center;
border-radius: 10px;
`;
// display는 하이라이라트와 닷을 겹치게 하기위해
const HighLight = styled.div`
background: #673ab7;
transition: 1s;
width: ${(props) => props.width};
height: 20px;
border-radius: 10px;
`;
const Dot = styled.div`
width: 40px;
height: 40px;
background: #fff;
border: 5px solid #673ab7;
border-radius: 40px;
margin-left: -20px;
`;
export default Progress;
.redux/modules/bucket.js
import {db} from "../../firebase"
import {
collection,
doc,
getDoc,
getDocs,
addDOc,
updateDoc,
deleteDoc,
addDoc,
} from "firebase/firestore"
import { async } from "@firebase/util";
// Actions
const LOAD = "bucket/LOAD";
const CREATE = "bucket/CREATE";
const DELETE = "bucket/DELETE";
const UPDATE = "bucket/UPDATE";
// 초기 상태값을 만들어줍니다.
const initialState = {
list: [],
// list: ["영화관 가기", "매일 책읽기", "수영 배우기"],
};
// 액션 생성 함수예요.
// 액션을 만들어줄 함수죠!
export function loadBucket(bucket_list) {
return { type: LOAD, bucket_list };
};
export function createBucket(bucket) {
return { type: CREATE, bucket: bucket };
}
export function updateBucket(bucket_index) {
return { type: UPDATE, bucket_index };
}
export function deleteBucket(bucket_index) {
return { type: DELETE, bucket_index };
}
// middlewares
export const loadBucketFB = () => {
return async function (dispatch) {
const bucket_data = await getDocs(collection(db, "bucket"));
let bucket_list = [];
bucket_data.forEach((b) => {
bucket_list.push({id:b.id, ...b.data()});
});
dispatch(loadBucket(bucket_list));
}
}
export const addBucketFB = (bucket) => {
return async function (dispatch) {
const docRef = await addDoc(collection(db, "bucket"), bucket);
const bucket_data = {id: docRef.id, ...bucket};
dispatch(createBucket(bucket_data));
}
}
export const updateBucketFB = (bucket_id) => {
return async function (dispatch, getState) {
const docRef = doc(db, "bucket", bucket_id);
await updateDoc(docRef, {completed: true});
const _bucket_list = getState().bucket.list;
const bucket_index = _bucket_list.findIndex((b) => {
return b.id === bucket_id;
})
dispatch(updateBucket(bucket_index));
}
}
export const deleteBucketFB = (bucket_id) => {
return async function (dispatch, getState) {
if(!bucket_id){
window.alert("아이디가 없네요")
return;
}
const docRef = doc(db, "bucket", bucket_id);
await deleteDoc(docRef);
const _bucket_list = getState().bucket.list;
const bucket_index = _bucket_list.findIndex((b) => {
return b.id === bucket_id;
})
dispatch(deleteBucket(bucket_index));
}
}
// 리듀서예요.
// 실질적으로 store에 들어가 있는 데이터를 변경하는 곳이죠!
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case "bucket/LOAD":
return { list: action.bucket_list };
case "bucket/CREATE": {
const new_bucket_list = [...state.list, action.bucket];
return { list: new_bucket_list };
}
case "bucket/UPDATE": {
const new_bucket_list = state.list.map((l, idx) => {
if(parseInt(action.bucket_index) === idx) {
return {...l, completed: true};
} else {
return l
}
})
return {list: new_bucket_list}
}
case "bucket/DELETE": {
const new_bucket_list = state.list.filter((l, idx) => {
return parseInt(action.bucket_index) !== idx;
});
return { list: new_bucket_list };
}
default:
return state;
}
}
.redux/configStore.js
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
import bucket from "./modules/bucket";
import thunk from "redux-thunk"
const middlewares = [thunk];
// root 리듀서를 만들어줍니다.
// 나중에 리듀서를 여러개 만들게 되면 여기에 하나씩 추가해주는 거예요!
const rootReducer = combineReducers({ bucket });
const enhancer = applyMiddleware(...middlewares)
// 스토어를 만듭니다.
const store = createStore(rootReducer, enhancer);
export default store;
firebase.js
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyCZDU77aDlBAOJd77H7VMbkTQ59hZrfRno",
authDomain: "sparta-bucket-cdb0b.firebaseapp.com",
projectId: "sparta-bucket-cdb0b",
storageBucket: "sparta-bucket-cdb0b.appspot.com",
messagingSenderId: "921157462850",
appId: "1:921157462850:web:2a41da30dbbfb4466fb6db",
measurementId: "G-GQS1WDBR2W"
};
initializeApp(firebaseConfig);
// Initialize Firebase
// const app = initializeApp(firebaseConfig);
export const db = getFirestore();
'React' 카테고리의 다른 글
리액트 4주차 mydictionary 2022-01-28 기능구현 완료! (0) | 2022.01.29 |
---|---|
React 패키지 설치 (0) | 2022.01.27 |
리액트 3주차 버킷리스트 만들기 연습 2022-01-25 (0) | 2022.01.25 |
리액트 3주차 평점 만들기 개인과제 2022-01-24 진행상황 (0) | 2022.01.24 |
리액트 3주차 개인과제 2022-01-23 진행상황 (0) | 2022.01.23 |