이번에는 퀴즈를 풀고나서 점수를 확인하고 대상에게 한마디를 남기고 랭킹정보를 볼 수 있도록 화면을 만들어보자
이때, 각화면을 라우팅시키고 데이터를 리덕스에넣어 적용시켜보자
1. 뷰 만들기
- Ranking.js
- Message.js
잊지않도록 기본형태는 다시한번 짚고넘어가자.
* Ranking.js
import React from "react";
import styled from "styled-components";
const Ranking = (props) => {
return (
<div>
position fixed :제목부분
</div>
<div>
list
list
... map 돌려서 랭킹정보를 보여준다.
</div>
<button></button>
)
}
export default Ranking;
Message.js는 Start.js와 내용만다르고 똑같은 형태이기때문에 시작페이지를 활용하자.
2. App.js 라우트 적용하기
- 패키지 설치하기
yarn add react-router-dom
yarn add redux react-redux
참고 공식문서!
https://reactrouter.com/web/guides/primary-components
※ 라우팅이란?
브라우저 주소에 따라 다른 페이지를 보여주는걸 라우팅이라고한다.
- index.js 브라우저라우터 적용하기
import { BrowserRouter } from "react-router-dom";
...
// 이부분이 index.html에 있는 div#root에 우리가 만든 컴포넌트를 실제로 랜더링하도록 연결해주는 부분입니다.
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root") );
※ BrowserRouter란?
웹 브라우저가 가지고 있는 주소 관련 정보를 props로 넘겨주는 친구. 현재 내가 어느 주소를 보고 있는 지 쉽게 알 수 있게 도와준다!
- App.js 라우터 적용하기
먼저 가장 중요한 임포트!!
import {Route, Switch} from "react-router-dom";
import { withRouter } from "react-router";
※ Route란?
컴포넌트에 path 속성을 이용하여 원하는 url를 지정하고 그 url에 접속하면 해당 컴포넌트만 렌더링이 된다.
즉, 사용자의 경로에 따라 다른 컴포넌트를 보여줄 수 있다.
※ Switch 란?
Route로 생성된 자식컴포넌트 중 매칭되는 첫번째 Route를 렌더링해준다.
보통 메인 Route의 url경로를 "/"로 지정하고 다른 Route에는 "/detail", "/login" 이런식으로 지정하게 되는데,
만약 Switch를 사용하지 않으면 그냥 메인페이지에 가기 위하여 "/" 경로를 접속하면
"/"가 포함된 "/detail", "/login" 컴포넌트들이 다 렌더링 되는 경우가 발생한다.
※ withRouter 란?
라우트 컴포넌트가 아닌곳에서 match / location / history 를 사용할 수 있게 도와준다.
(컴포넌트에서 라우터로 접근하는 방법 중 하나)
기존 작성을 했었던 조건부렌더링은 지워주고 본격적으로 라우터 적용을 해보자.
render () {
return (
<div className="App">
<Switch>
<Route path="/quiz" component={Quiz} />
<Route path="/" exact component={Start} /> //exact 는 주소가 정확한지 확인해주는친구.
<Route path="/score" component={Score} />
<Route path="/message" component={Message} />
<Route path="/ranking" component={Ranking} />
</Switch>
</div>
);
}
}
※ Route 사용방법 복습!!
1. 넘겨줄 props 가 없을때 (=우정테스트)
<Route path="주소[/home 처럼 /와 주소를 적어요]" component={[보여줄 컴포넌트]}/>
2. 넘겨줄 props가 있을때 (=버킷리스트)
<Route path="주소[/home 처럼 /와 주소를 적어요]" render={(props) => (<BucketList list={this.state.list} />)} />
마지막으로 연결해주면 끝!
export default withRouter(App);
3. 리덕스 연결하기
- 폴더 생성
→ src > redux > modules
store를 먼저 만들어야 컴포넌트랑 연결할 수 있기때문에 모듈을 먼저 만들고 시작해보자.
quiz.js : Quiz.js에 대한 모듈
* 리덕스(redux) 기본개념
1) Action : 어플리케이션의 store(스토어), 즉 저장소로 data를 보내는 방법이다.
view에서 정의되어있는 액션을 호출하면 action creators(액션 생성자)는 어플리케이션의 state(상태)를 변경하여 준다.
action type은 action creators(액션 생성자)를 통해 사용된다.
2) Reducer : action을 통해 어떠한 행동을 정의했다면, 그 결과 상태가 어떻게 바뀌는지 특정하게 하는 함수
3) Store : “무엇이 일어날지”를 나타내는 action,
action에 따라 상태를 수정하는 reducer를 저장하는 어플리케이션에 있는 단 하나의 객체
* quiz.js 완성코드
// Actions // 퀴즈 데이터 가져온다 const GET_QUIZ = "quiz/GET_QUIZ"; // 유저의 응답(퀴즈 답)을 추가한다 const ADD_ANSWER = "quiz/ADD_ANSWER"; // 응답을 초기화 해준다 const RESET_ANSWER = "quiz/RESET_ANSWER"; const initialState = { name: "손흥민", score_texts: { 60: "앞으로 더 알아갈 필요가 있겠어!", 80: "너에 대해 모든걸 더 알고싶어!!", 100: "영혼의단짝!", }, answers: [], quiz: [ { question: "손흥민의 소속팀은 토트넘이다.", answer: "O" }, { question: "손흥민의 프로데뷔년도는 2011년이다.", answer: "X" }, { question: "현재 소속팀의 등번호 7번이다.", answer: "O" }, { question: "저번시즌 17골 득점 기록을 보유하고있다.", answer: "O" }, { question: "손흥민은 인싸다.", answer: "O" }, ], }; // Action Creators export const getQuiz = (quiz_list) => { return { type: GET_QUIZ, quiz_list }; }; export const addAnswer = (answer) => { return { type: ADD_ANSWER, answer }; }; export const resetAnswer = () => { return { type: RESET_ANSWER }; } // Reducer export default function reducer(state = initialState, action = {}) { switch (action.type) { // do reducer stuff case "quiz/GET_QUIZ": { return { ...state, quiz: action.quiz_list }; } case "quiz/ADD_ANSWER": { return { ...state, answers: [...state.answers, action.answer] }; } case "quiz/RESET_ANSWER": { return {...state, answers: []}; } default: return state; } } |
* rank.js 완성코드
import {firestore} from "../../firebase"; const rank_db = firestore.collection("rank"); // Actions // 유저 이름을 바꾼다 const ADD_USER_NAME = "rank/ADD_USER_NAME"; // 유저 메시지를 바꾼다 const ADD_USER_MESSAGE = "rank/ADD_USER_MESSAGE"; // 랭킹정보를 추가한다 const ADD_RANK = "rank/ADD_RANK"; // 랭킹정보를 가져온다 const GET_RANK = "rank/GET_RANK"; const IS_LOADED = "rank/IS_LOADED"; const initialState = { user_name: "", user_message: "", user_score: "", score_text: { 60: "앞으로 더 알아갈 필요가 있겠어!", 80: "너에 대해 모든걸 더 알고싶어!!", 100: "영혼의단짝!", }, ranking: [], is_loaded: false, }; // Action Creators export const addUserName = (user_name) => { return { type: ADD_USER_NAME, user_name }; }; export const addUserMessage = (user_message) => { return { type: ADD_USER_MESSAGE, user_message }; }; export const addRank = (rank_info) => { return { type: ADD_RANK, rank_info }; }; export const getRank = (rank_list) => { return { type: GET_RANK, rank_list }; }; export const isLoaded = (loaded) => { return {type: IS_LOADED, loaded}; } export const addRankFB = (rank_info) => { return function (dispatch) { // 데이터를 저장할 동안 스피너가 뜨도록 해줍시다. dispatch(isLoaded(false)); let rank_data = { message: rank_info.message, name: rank_info.name, score: rank_info.score, }; rank_db.add(rank_data).then((doc) => { // id를 콘솔로 확인해볼까요? console.log(doc.id); // id를 추가해요! // current는 여기서 추가해줘야 해요! 그래야 내가 한 것만 하이라이트를 줄 수 있거든요. (db에 current가 true로 들어가면 안됩니다!) rank_data = { ...rank_data, id: doc.id, current: true }; // 데이터를 추가해줘요! dispatch(addRank(rank_data)); }); }; } export const getRankFB = () => { return function (dispatch){ dispatch(isLoaded(false)); rank_db.get().then((docs) => { let rank_data = []; docs.forEach((doc) => { // console.log(doc.data()); rank_data = [...rank_data, {id: doc.id, ...doc.data()}]; }); dispatch(getRank(rank_data)); dispatch(isLoaded(true)); }) } } // Reducer export default function reducer(state = initialState, action = {}) { switch (action.type) { // do reducer stuff case "rank/ADD_USER_NAME": { return { ...state, user_name: action.user_name }; } case "rank/ADD_USER_MESSAGE": { return { ...state, user_message: action.user_message }; } case "rank/ADD_RANK": { return { ...state, ranking: [...state.ranking, action.rank_info] }; } case "rank/GET_RANK": { // 리덕스에 있던 데이터에 파이어베이스에서 가져온 데이터를 추가해요! 다만, 같은 값이 있으면 안되겠죠?? // 중복되지 않은 데이터만 추가해줄거예요. // id가 같은 지 아닌 지로 데이터를 구분해서 추가해볼게요. // 일단 랭킹 데이터를 담을 변수를 만들고, 기존 리덕스 값을 가져다가 넣어줍니다. let ranking_data = [...state.ranking]; // 랭킹 데이터의 id 배열을 하나 만들어줍니다. const rank_ids = state.ranking.map((r, idx) => { return r.id; }); // 콘솔로 확인해볼까요! :) console.log(rank_ids); // 리덕스에 없는 데이터만 가져오기 const rank_data_fb = action.rank_list.filter((r, idx) => { // 가지고 온 값의 id가 리덕스에 있는 아이디 배열에 없으면 추가해요! if(rank_ids.indexOf(r.id) === -1){ // 배열에도 이렇게 스프레드 문법을 사용할 수 있습니다. :) (다른 방법으로 추가하셔도 됩니다.) ranking_data = [...ranking_data, r]; } }); // 데이터 확인해보기! console.log(ranking_data); return { ...state, ranking: ranking_data }; } case "rank/IS_LOADED": { return {...state, is_loaded: action.loaded}; } default: return state; } } |
'내마음대로만들어보자 > React' 카테고리의 다른 글
[Progress] 우정테스트 사이트 만들기(Feat.리액트) (0) | 2021.08.12 |
---|---|
[Store] 우정테스트 사이트 만들기(Feat.리액트) (0) | 2021.08.11 |
[EventListener 코드설명] 우정테스트 사이트 만들기(Feat.리액트) (0) | 2021.08.09 |
[Swipe] 우정테스트 사이트 만들기(Feat.리액트) (0) | 2021.08.08 |
[조건부렌더링/state 데이터 관리] 우정테스트 사이트 만들기(Feat.리액트) (0) | 2021.08.08 |