본문 바로가기
React

퀴즈 화면, 정답 화면 만들기/리액트 기초 2주차 과제

by imagineer_jinny 2021. 4. 13.

시도하려했으나 fail..

 

막힌 부분들:

 

1. App.js에서 state 받아와서 Quiz.js에서 인덱스 뜨게 하는 것

: console.log에서 확인해보면 잘 받아와는 지는데 퀴즈 화면에 왜 띄우질 못하니..

일단 유즈스테이트가 너무 헷갈림. 

또 다시 유즈스테이트 안에 초기값 넣어주는게 헷갈림.

초기값을 임의로 넣어주지 않고 state에서 받아온 애 중 첫번째를 넣어주고 싶은건데..

 

해결: 인덱스는 리스트가 아니라서 쉽게 성공?했는데 리스트가 문제임. 

리스트 my_list[0].question 넣어서 해결 ㅎ 

 

2. 뜨게 하면 map 돌려서 ++해주기

인덱스는 애초에 1로 하나만 해줘서 리스트가 아님. 근데 맵을 어떻게 돌리지...?에서 막힘

 

map 쉬운거말고.. state에서 받아온 리스트들을 맵을 이용해서 차례차례 보여주고 싶은데 그걸 어떻게 써야할지 모르겠음.. 에로우도 나오고... 복잡한 기호들 나올때마다 머리가 아프다. 일단 에로우 펑션 말 나온김에 민규님이 보내준 링크를 한번 봐보자. 그리고 강의에서 map들을 어떻게 썼는지 모양들을 살펴보자.

www.youtube.com/watch?v=e_lU39U-5bQ&t=588s

왼쪽꺼 따라쳤는데 에러가 이렇게 뜨네.ㅎ

reactjs.org/docs/error-boundaries.html

 

Error Boundaries – React

A JavaScript library for building user interfaces

reactjs.org

답안코드 참고해서 첫번째 값은 뜨긴 뜨는 것 확인..

근데 맵 돌려서 어떤 행위를 하면 계속 다음거가 뜨면 좋겠는데 지금은 일단 함수로 다음거 뜨게 하고 있음.

문제는 2번까지가 최대라는 것임...

사라지지 않는 의문.. 맵을 어떻게 돌리나..?

 

 

3. 마우스 드래그 기능

찾아보기 귀찮음.

애니메이션 다큐먼트? 찾아봐야 할 것 같은데..

Element: mouseenter event - Web APIs | MDN (mozilla.org)

 

Element: mouseenter event - Web APIs | MDN

Element: mouseenter event The mouseenter event is fired at an Element when a pointing device (usually a mouse) is initially moved so that its hotspot is within the element at which the event was fired. Bubbles No Cancelable No Interface MouseEvent Event ha

developer.mozilla.org

이걸 활용해서 만들어보자.

App.js에서 마우스이벤트 사용해서 만드는데 에러뜨고 답 보니까 App.js에 안만들고 다른 컴포넌트 만들어줌. 

딱봐도 복잡해보여서 그냥 답안 영상 보기로..!

 

4. 페이지 하나씩은 만들겠는데 한꺼번에 써주면 다 보이는데.. 

나중에 라우팅처리 배우면 할까..

 


Quiz.js

 

1. 화면부터 나누기

 

2. styled-component로 하나씩 바꿔가며 스타일 잡아주기

 

 

+자꾸 flex 하는데 flex가 뭐야?

flex - CSS: Cascading Style Sheets | MDN (mozilla.org)

 

flex - CSS: Cascading Style Sheets | MDN

flex flex CSS 속성은 하나의 플렉스 아이템이 자신의 컨테이너가 차지하는 공간에 맞추기 위해 크기를 키우거나 줄이는 방법을 설정하는 속성입니다. flex는 flex-grow, flex-shrink, flex-basis의 단축 속성

developer.mozilla.org


Score.js

 

1. 화면부터 나누기

 

 

2. styled-component로 하나씩 바꿔가며 스타일 잡아주기

 


 

Score과 Quiz 페이지를 만들었으니 이제 조건부렌더링을 해줄 차례!

+부모 컴포넌트에서 자식 컴포넌트로 주는 데이터 가져다 쓰는 방법도 할 것!

 

조건부 렌더링하려면? App.js로 돌아가서 page라는 state값 하나 만들어주기.

이후 조건을 걸어서 각 조건에 맞는 컴포넌트를 보여주면 됨.

이후 퀴즈 리스트들을 넘겨주고 퀴즈에서는 프롭스로 리스트를 받아옴.

 

이제 잘 받아왔으니 Score에 점수별 텍스트 띄워보기 도전!

맨처음에 뭘 해야 하나?

App.js에다 변수명 만드는 것 부터!


스와이프: tinder-card 패키지 사용해서 해보기

 

0. 패키지 설치

 

yarn add react-tinder-card

 

1. App.js에 패키지 임포트 부터 해주기

import TinderCard from "react-tinder-card";

2. Tinder card가 뭐야? 공식문서 보러 가기!

react-tinder-card - npm (npmjs.com)

 

react-tinder-card

A npm react module for making react elements swipeable like in the dating app tinder.

www.npmjs.com

3. 일단 틴더카드가 필요하긴 한데 그 전에 생각해볼 것. 우리 질문 몇개 만들었더라? 

네개 만들었음. 그럼 질문 네 개 띄우는 방법? map돌리기!

대충 map을 돌려야한다는 느낌은 알겠음. 근데 어디서 어떻게 돌려?

 

4. 그럼 다시 생각해보자. map을 왜 돌려? 

틴더카드 이용해서 르탄이 이미지 스와이프 해줄때마다 질문을 바뀌게 할거니까.

그럼 아, 질문과 이미지 스와이프가 관련이 있다라는 것을 이해할 수 있고

이미지가 스와이프될 때 맵도 같이 돌려져서 질문도 바뀐다라고 정리할 수 있음.

그럼 일단 이미지 단위가 어디에 있는지 확인하는거야. Quiz.js의 <DragItem>에 싸여있네.

다시말하면 <DragItem>가 스와이프 할 영역임. 그럼 어떻게 해야한다? 이 친구 자체를 맵 돌려야 한다!

5. map 써보기.. 키값 써주는거 잊지 말고

6. 그리고 나서 보면 이미지가 개많이생김. 우리는 하나하나 뜨기를 원하는데!

그럼 어떻게 해주냐? 

지금 내가 풀고 있는 문제의 번호가 있을 것인데 그 번호랑 일치하는 순번의 친구만 보여줄 것임!

7. 그럼 뭐부터 해야해?

state를 사용해야함! 왜??

순번 넘어가는거 하나 하나 저장해놓을 친구가 필요하니까!

얘도 고쳐주기

8. 이제 스와이프 해보기!!

스와이프에서 O로 가든 X로 가든 문제를 푼건데 문제를 풀었으면 num을 +1 해줘야함.

이걸 기억하고 틴더카드 쓰기로 넘어가보자!

틴더카드 써줘서 하나하나 넘길 때 마다 num을 +1 +1 해주면 됨.

어떻게 +1 +1 해줘? 만들어둔 useState함수 있잖아.

거기 setNum을 가지고 +1 해주면 되겠구나 하는 생각이 지금쯤은 진짜 나야함!

 

9. 틴더카드 쓰는 방법: 공식문서 Usage에서 보고오기.

10. 함수 복붙해오고 TinderCard 컴포넌트로 교체해주기

11. 르탄이 이미지를 오른쪽으로 밀어버리니까 이런 콘솔이 나타남

12. 함수를 가서 보면 ()안의 direction이 어느쪽으로 스와이프됬는지 방향을 알려주는것을 알 수 있음.

그럼 여기서 우리는 일단 setNum 해가지고 +1해서 다음 문제로 넘어가보는것을 시도해볼 수 있음!

13. 성공!!

 

import React from "react";
import TinderCard from "react-tinder-card";
import styled from "styled-components";
import img from "./scc_img01.png";

const Quiz =(props)=>{

    const [num,setNum]=React.useState(0);

    const list=props.list;

    const onSwipe = (direction) => {
        setNum(num+1);
        console.log('You swiped: ' + direction)
        
      }

    return (
        
        <QuizContainer> 
            <p><span>{num+1}번 문제</span></p>

            <Question>{props.list.map((l,idx)=>{
               if(num===idx)
               {
                //Question안에는 리스트 하나에 들어가있던 question을 넣어주기
                  return(<Question key={idx}>{l.question}</Question>); 
               }
            })}</Question>


            <AnswerZone>
                <Answer>O</Answer>
                <Answer>X</Answer>
            </AnswerZone>
            
            {/* 리스트 하나를 l이라고 하고 idx 데려오고 */}
            {props.list.map((l,idx)=>{
                if(idx===num)
                {
                    return(
                        <DragItem key={idx}>
                            <TinderCard onSwipe={onSwipe}>
                                <img src={img}/>
                            </TinderCard>
                        </DragItem>
                    );
                }

            })}
            
            
        </QuizContainer>
    );
}

const QuizContainer=styled.div`
text-align:center;
    &>p>span{
        padding:8px 16px;
        background-color:#fef5d4;
        border-radius:30px;      
    }
`;

const Question=styled.h1`
    font-size:1.5em;
`;

const AnswerZone =styled.div`
    width:100vw;
    height:100vh;
    display:flex;
    position:absolute; //백그라운드에 있어야하고 넓게 전체 영역 붙잡아도 상관 없음
    top: 0; //y좌표
    left:0;//x좌표
    z-index:1;
`;

const Answer=styled.div`
   width:50%;
   display:flex;
   justify-content:center;
   align-items:center;
   font-size:10em;
   font-weight:600; //두께
   color:#dadafc77;
`;

const DragItem=styled.div`
    position:fixed;
    top:0;
    left:0;
    width:100vw;
    height:100vh;
    z-index: 10; //AnswerZone보다 앞에 있어야하니까 10줌
    display:flex;
    align-items:center;
    justify-content:center;
    
    & > div{
        background-color:#ffd6aa;
        border-radius: 150px;
    }
    & img{
        max-width:150px; //이미지사이즈줄이기
    }
`;


export default Quiz;

직접 이벤트 리스너 가지고 터치 이벤트로 스와이프 도전해보기!

 

일단 터치 이벤트를 보고 싶다? 

구글에 mdn touch event 쳐서 공식문서 보기

Touch events - Web API | MDN (mozilla.org)

 

Touch events - Web API | MDN

Touch events 일부분만 번역함.  터치를 기반으로 한 양질의 서비스를 제공하기 위해, Touch Events(터치이벤트)는 터치로 인한 움직임을 감지할 능력을 제공합니다. 터치 이벤트 인터페이스는 상대적

developer.mozilla.org

1. 스와이프되는 저 영역을 위해 새로운 파일을 만들것임

2. SwipeItem.js

 

React.memo() 기능 사용

Memoization is a programming technique for caching the results of previous expensive computations so that they can be quickly retrieved without repeated effort when the same inputs are passed.

 

여러번 렌더가 일어나지 않도록 방어해주는 친구!

const MyComponent = React.memo(function MyComponent(props) {
  /* props를 사용하여 렌더링 */
});

React 최상위 API – React (reactjs.org)

 

React 최상위 API – React

A JavaScript library for building user interfaces

ko.reactjs.org

//onSwipe로 Swipe했을 때만 일어나는 어떤 액션 있는 것만 받아올것임
const SwipeItem = React.memo(({ onSwipe })

//defaultProps만들어주기
SwipeItem.defaultProps = {
  onSwipe: (direction) => {},
};

defaultProps

우리가 어떤 props를 받아다가 component 안에서 쓰는데 props가 없을 때가 있을 수도 있음.

이러면 에러가 나니까 이 에러 방지를 위해서 기본적으로 이 props는 어떤 값을 가지고 있어라는걸 알려주는 친구임.

 

3. SwipeItems라는 컴포넌트를 만들어줬으면 퀴즈에서 불러다 써야하니까 퀴즈에 임포트 해주기

4. 다시 돌아와서 SwipeItem에서 뭘 해줘야하나? 터치 이벤트 먼저 잡아주기!

1)터치 시작했을 때(누를 때)

2)터치 무브 할 때(누른 상태에서 움직일 때)

3)터치 뗐을 때(터치 앤드)

4)터치 캔슬(이벤트가 중간에 끊겼을 때에 호출되는 친구)

 

5. 여기 DragItem에 addEventListener 해주고 싶으면 어떻게 해야할까?

일단 ref 잡아줘야함!!

6. ref 잡아줬으면 뭐 해야해?

addEventListener 해주면 되는데 이벤트 리스너를 등록 할 때는 무조건  한번만 하면됨.

그러면 React.useEffect!

컴포넌트가 렌더링될 때마다 어떠한 작업을 수행하도록 해야 한다면, 그것을 설정하는 Hook으로 useEffect를 사용합니다.  componentDidMout, componentDidUpdate의 기능을 합친 형태가 useEffect로 볼 수 있습니다.

chanhuiseok.github.io/posts/react-10/

 

[React] 리액트 Hooks (1) - useState, useEffect

컴퓨터/IT/알고리즘 정리 블로그

chanhuiseok.github.io

 

여기에 어떤 함수가 올거고 처음 한번만 할꺼면 대괄호.

그리고 이제 내가 하고 싶은 행동을 함수로 만들어주면 됨! 

React.useEffect를 썼으면 처음 한번 이벤트 리스너 등록을 해야하는데 

터치 스타트 할 때, 터치 앤드 할 때, 무브할 때, 캔슬 할 때 각각 뭘 할건지 정해주는 함수가 필요함.

 

이 함수를 먼저 만들어 줄 것임!

껍데기부터 ㅎㅎ

 

7. 캔슬되었을 땐 아예 없는 일이 되야하니까

const reset, 터치 시작할 땐 touchStart, 터치 끝났을 땐 touchEnd, 움직일 때 touchMove

 터치 캔슬되었을 때 함수 하나 더 만들기. touchCancle 함수들 빈껍데기만 만들어주고

 

8. 이벤트 리스너 등록하기

9. 이벤트 리스너 등록하면 뭐도 해준다? 해제도 해준다!

10. 다음에 하나씩 함수 채워주기~

 

-터치 시작 좌표 저장할 변수부터 만들자.

그리고 어떤 상태인지 알려줄 변수랑 target_classname변수도 만들어서

애니메이팅 효과 줘보자.

 

11. 함수 채우는 것 부터는 4주차 애니메이션 관련 강의 듣고 다시 해보려면 하기!

댓글