스파르타 코딩클럽 - 리액트 5주차(5)
1. Quiz_버킷리스트 생성 시 스피너 띄우기.
1-1. Firestore에 데이터 추가하면 스피너를 띄워보자.
추가하기 버튼을 누르면 → 로딩 스피너를 띄우고 → 추가가 끝나면 → 페이지를 보여준다.
- App.js
import React from "react";
import { withRouter } from "react-router";
import { Route, Switch } from "react-router-dom";
// import [컴포넌트 명] from [컴포넌트가 있는 파일경로];
import BucketList from "./BucketList";
import styled from "styled-components";
import Detail from "./Detail";
import NotFound from "./NotFound";
// 리덕스 스토어와 연결하기 위해 connect라는 친구를 호출할게요!
import { connect } from "react-redux";
// 리덕스 모듈에서 (bucket 모듈에서) 액션 생성 함수 두개를 가져올게요!
import {
loadBucket,
createBucket,
loadBucketFB,
addBucketFB,
} from "./redux/modules/bucket";
import Progress from "./Progress";
import Spinner from "./Spinner";
// firestore 가져오기
import { firestore } from "./firebase";
// 이 함수는 스토어가 가진 상태값을 props로 받아오기 위한 함수예요.
const mapStateTopProps = (state) => ({
bucket_list: state.bucket.list,
is_loaded: state.bucket.is_loaded,
});
// 이 함수는 값을 변화시키기 위한 액션 생성 함수를 props로 받아오기 위한 함수예요.
const mapDispatchToProps = (dispatch) => ({
load: () => {
dispatch(loadBucketFB());
},
create: (new_item) => {
console.log(new_item);
dispatch(addBucketFB(new_item));
},
});
// 클래스형 컴포넌트는 이렇게 생겼습니다!
class App extends React.Component {
constructor(props) {
super(props);
// App 컴포넌트의 state를 정의해줍니다.
this.state = {};
// ref는 이렇게 선언합니다!
this.text = React.createRef();
}
componentDidMount() {
this.props.load();
}
addBucketList = () => {
const new_item = this.text.current.value;
this.props.create(new_item);
};
// 랜더 함수 안에 리액트 엘리먼트를 넣어줍니다!
render() {
// 콘솔로 확인해요!
console.log(this.props.is_loaded);
return (
<div className="App">
<Container>
<Title>내 버킷리스트</Title>
{/* firestore에서 데이터를 가져온 후에만 페이지를 보여줄거예요! */}
{!this.props.is_loaded ? (
<Spinner />
) : (
<React.Fragment>
<Progress />
<Line />
<Switch>
<Route path="/" exact component={BucketList} />
<Route path="/detail/:index" component={Detail} />
<Route component={NotFound} />
</Switch>
</React.Fragment>
)}
</Container>
<Input>
<input type="text" ref={this.text} />
<button onClick={this.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;
align-items: center;
justify-content: space-between;
& > * {
padding: 5px;
}
& input {
border-radius: 5px;
margin-right: 10px;
border: 1px solid #888;
width: 70%;
&:focus {
border: 1px solid #a673ff;
}
}
& button {
width: 25%;
color: #fff;
border: 1px solid #a673ff;
background-color: #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: #673ab7;
text-align: center;
`;
const Line = styled.hr`
margin: 16px 0px;
border: 1px dotted #ddd;
`;
// withRouter 적용
// connect로 묶어줬습니다!
export default connect(mapStateTopProps, mapDispatchToProps)(withRouter(App));
- bucket.js
import { firestore } from "../../firebase";
// Actions
const LOAD = "bucket/LOAD";
const CREATE = "bucket/CREATE";
const DELETE = "bucket/DELETE";
const UPDATE = "bucket/UPDATE";
// is loaded
const LOADED = "bucket/LOADED";
const initialState = {
is_loaded: false,
list: [
{ text: "영화관 가기", completed: false },
{ text: "매일 책읽기", completed: false },
{ text: "수영 배우기", completed: false },
],
};
const bucket_db = firestore.collection("bucket");
// Action Creators
export const loadBucket = (bucket) => {
return { type: LOAD, bucket };
};
export const createBucket = (bucket) => {
return { type: CREATE, bucket };
};
export const deleteBucket = (bucket) => {
return { type: DELETE, bucket };
};
export const updateBucket = (bucket) => {
return { type: UPDATE, bucket };
};
// loaded를 받아서 is_loaded 값을 true/false로 바꿔줄 액션 생성 함수입니다.
export const isLoaded = (loaded) => {
return {type: LOADED, loaded}
}
// 파이어베이스랑 통신하는 부분
export const loadBucketFB = () => {
return function (dispatch) {
bucket_db.get().then((docs) => {
let bucket_data = [];
docs.forEach((doc) => {
// 도큐먼트 객체를 확인해보자!
console.log(doc);
// 도큐먼트 데이터 가져오기
console.log(doc.data());
// 도큐먼트 id 가져오기
console.log(doc.id);
if (doc.exists) {
bucket_data = [...bucket_data, { id: doc.id, ...doc.data() }];
}
});
console.log(bucket_data);
// 이제 액션이 디스패치 되도록 해줍시다! 그러면 끝!
dispatch(loadBucket(bucket_data));
});
};
};
export const addBucketFB = (bucket) => {
return function (dispatch) {
// 요청 보내기 전에 스피너를 보여줍시다
dispatch(isLoaded(false));
// 생성할 데이터를 미리 만들게요!
let bucket_data = { text: bucket, completed: false };
// add()에 데이터를 넘겨줍시다!
bucket_db
.add(bucket_data)
.then((docRef) => {
// id를 추가한다!
bucket_data = { ...bucket_data, id: docRef.id };
console.log(bucket_data);
// 성공했을 때는? 액션 디스패치!
dispatch(createBucket(bucket_data));
// 스피너도 다시 없애줘야죠!
dispatch(isLoaded(true));
})
.catch((err) => {
// 여긴 에러가 났을 때 들어오는 구간입니다!
console.log(err);
window.alert("오류가 났네요! 나중에 다시 시도해주세요!");
// 스피너도 다시 없애줘야죠!
dispatch(isLoaded(true));
});
};
};
export const updateBucketFB = (bucket) => {
return function (dispatch, getState) {
// state에 있는 값을 가져옵니다!
const _bucket_data = getState().bucket.list[bucket];
// id가 없으면? 바로 끝내버립시다.
if (!_bucket_data.id) {
return;
}
// 새로운 값을 만들어요!
let bucket_data = { ..._bucket_data, completed: true };
bucket_db
.doc(bucket_data.id)
.update(bucket_data)
.then((res) => {
dispatch(updateBucket(bucket));
})
.catch((err) => {
console.log("err");
});
};
};
export const deleteBucketFB = (bucket) => {
return function (dispatch, getState) {
const _bucket_data = getState().bucket.list[bucket];
// id가 없으면? 바로 끝내버립시다.
if (!_bucket_data.id) {
return;
}
// 삭제하기
bucket_db
.doc(_bucket_data.id)
.delete()
.then((res) => {
dispatch(deleteBucket(bucket));
})
.catch((err) => {
console.log("err");
});
};
};
// Reducer
export default function reducer(state = initialState, action) {
switch (action.type) {
// do reducer stuff
case "bucket/LOAD": {
if (action.bucket.length > 0) {
return { list: action.bucket, is_loaded: true };
}
return state;
}
case "bucket/CREATE": {
const new_bucket_list = [...state.list, action.bucket];
return { ...state, list: new_bucket_list };
}
case "bucket/DELETE": {
const bucket_list = state.list.filter((l, idx) => {
if (idx !== action.bucket) {
return l;
}
});
return { ...state, list: bucket_list };
}
case "bucket/UPDATE": {
const bucket_list = state.list.map((l, idx) => {
if (idx === action.bucket) {
return { ...l, completed: true };
}
return l;
});
return { ...state, list: bucket_list };
}
case "bucket/LOADED": {
return {...state, is_loaded: action.loaded};
}
default:
return state;
}
}
Author And Source
이 문제에 관하여(스파르타 코딩클럽 - 리액트 5주차(5)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@tmdals3785/스파르타-코딩클럽-리액트-5주차5저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)