React.js

初心者向けのReact.jsで超簡単なクイズアプリを作る方法

初心者向けのReact.jsで超簡単なクイズアプリを作る方法

みなさんこんにちは、からんです。

今回はReact.js製のクイズアプリの作り方の解説をします。

仕様

最初が下記です。

解答をクリックすると正解、不正解の表示が出ます。

全ての問題の解答が終わったら全部の問題中何問正解かが表示されます。

デモ

デモはここから見ることができます。

css

Reactをインストールしたらcssをindex.cssに記述します。

index.cssのコードは下記にします。

/*--------------------------------ここからreset.css----------------------------------------------------------*/
/*
html5doctor.com Reset Stylesheet
v1.6.1
Last Updated: 2010-09-17
Author: Richard Clark - http://richclarkdesign.com
Twitter: @rich_clark
*/

html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
    margin:0;
    padding:0;
    border:0;
    outline:0;
    font-size:100%;
    vertical-align:baseline;
    background:transparent;
}

body {
    line-height:1;
}

article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
    display:block;
}

nav ul {
    list-style:none;
}

blockquote, q {
    quotes:none;
}

blockquote:before, blockquote:after,
q:before, q:after {
    content:'';
    content:none;
}

a {
    margin:0;
    padding:0;
    font-size:100%;
    vertical-align:baseline;
    background:transparent;
}

/* change colours to suit your needs */
ins {
    background-color:#ff9;
    color:#000;
    text-decoration:none;
}

/* change colours to suit your needs */
mark {
    background-color:#ff9;
    color:#000;
    font-style:italic;
    font-weight:bold;
}

del {
    text-decoration: line-through;
}

abbr[title], dfn[title] {
    border-bottom:1px dotted;
    cursor:help;
}

table {
    border-collapse:collapse;
    border-spacing:0;
}

/* change border colour to suit your needs */
hr {
    display:block;
    height:1px;
    border:0;  
    border-top:1px solid #cccccc;
    margin:1em 0;
    padding:0;
}

input, select {
    vertical-align:middle;
}
p,.nav{
    margin:0;
}
/*-----------------------------------ここまでreset.css------------------------------------------------------------*/


li{
  list-style:none;
}
.App{
    width: 500px;
    margin:100px auto 0;
}
h1{
    font-size: 30px;
    text-align: center;
    margin-bottom: 40px;
}
h2{
    font-size: 20px;
    line-height:1.7em;
    text-align: center;
    color:red;
}
h2 span{
    font-size: 25px;
    color:black;
}
ul{
    margin-top: 30px;
    display: flex;
    justify-content:space-around;
}
li{
    margin-bottom: 10px;
    letter-spacing:2px;
    background-color:blue;
    display: inline-block;
    padding: 10px;
    color:#fff;
    border-radius:5px;
    cursor:pointer;
    transition:0.9s;
}
li:hover{
    opacity:0.6;
    transition:0.9s;
}
li:hover{
    opacity:0.6;
    transition:0.9s;
}
p{
    font-size: 25px;
    line-height:1.7em;
}
.correct{
    color:red;
}
button{
    background-color:skyblue;
    padding: 20px;
    color:#fff;
    margin: 40px auto 0;
    display: block;
    width: 100px;
    text-align: center;
    border-radius:10px;
    border:none;
}

コンポーネントの切り分け

今回のクイズですがコンポーネントの切り分けはこうします。

全体をAPP.jsに記述してボタンの部分をAnswer.jsxに記述をします。

Answer.jsxはまだ作ってないのでsrcディレクトリの下にcomponentsディレクトリを作ってその下に置きます。

最初の状態の表示

まず最初の状態にします。

App.jsのコードを下記にします。

import React from 'react';
import Answer from './components/Answer';

function App() {

  return (

    <div className="App">
      <Answer/>
    </div>
  );
}

export default App;

Answer.jsxのコードを下記にします。

import React from 'react';

    const Answer = () =>{
        return(
            <>
                <h1>3択クイズ</h1>
                  <h2><span>第1問</span><br></br>問題:数学Ⅲで一番難しい分野は</h2>
                <ul> 
                  <li>微分積分</li>
                  <li>ベクトル</li>
                  <li>2次曲線</li>
                </ul>
            </>
        );
    };

export default Answer;

第〜問・問題・選択肢が変わるようにする

App.jsのコードを下記にします。

import React,{useState} from 'react';                       //この行を修正
import Answer from './components/Answer';

function App() {

  //ここから追加
  const questions =[
    {
      questionText:'数学Ⅲで一番難しい分野は',
      answerOptions:[
        {answerText:'微分積分',isCorrect:true},
        {answerText:'ベクトル',isCorrect:false},
        {answerText:'2次曲線',isCorrect:false},
      ],
    },
    {
      questionText:'この中で難易度が低いプログラミング言語は',
      answerOptions:[
        {answerText:'React.js',isCorrect:false},
        {answerText:'Angular.js',isCorrect:false},
        {answerText:'HTML',isCorrect:true},
      ],
    },
    {
      questionText:'この中でYouTuberのゲーム実況者は',
      answerOptions:[
        {answerText:'Taiman',isCorrect:false},
        {answerText:'ガッチマン',isCorrect:true},
        {answerText:'ハクア',isCorrect:false},
      ],
    },
  ];

  const [currentQuestion,setCurrentQuestion] = useState(0);  

  const handleAnswerButtonClick = (isCorrect) => {

    const nextQuestion = currentQuestion + 1;

    if(nextQuestion < questions.length){
      setCurrentQuestion(nextQuestion);
    }
  }
  //ここまで追加

  return (

    <div className="App">
      <Answer
      
      
        //ここから追加
        handleAnswerButtonClick={handleAnswerButtonClick}
        questions={questions}  
        currentQuestion={currentQuestion}  
        //ここまで追加
        
        
      />
    </div>
  );
}

export default App;

Answer.jsxのコードを下記にします。

import React from 'react';

    const Answer = ({handleAnswerButtonClick,questions,currentQuestion}) =>{     //この行を修正
        return(
            <>
                <h1>3択クイズ</h1>
                <h2><span>{currentQuestion+1}</span><br></br>問題:{questions[currentQuestion].questionText}</h2>         //この行を修正
                <ul> 
          
                //ここから修正
                {
                    questions[currentQuestion].answerOptions.map((answerOption,key)=>(
                        <li 
                            key={key}
                            onClick={()=>handleAnswerButtonClick(answerOption.isCorrect)}>{answerOption.answerText}
                        </li>)
                    )
                }
                //ここまで編集
                  
                </ul>
            </>
        );
    };

export default Answer;

問題と解答の埋め込み方ですがまず配列で用意します。

App.jsの7行目〜32行目です。

そしてこれをAnswer.jsxの11行目〜18行目でmapメソッドを使って埋め込んでいます。

App.jsの52、53、54行目ですがApp.jsの7行目〜32行目をAnswer.jsxで使えるようにするため(親コンポーネントから子コンポーネントへ情報の受け渡し)に記述を追加しています。

Answer.jsxですが10行目にkey、12行目にkey={key}がありますがReactではliタグを付けるときは他と被らない番号をつけないといけないためにあります。

今のままでは正解、不正解を選ぶことができないので正解、不正解の判定と表示されるようにします。

正解・不正解の判定と表示

App.jsのコードを下記にします。

import React,{useState} from 'react';
import Answer from './components/Answer';

function App() {

  const questions =[
    {
      questionText:'数学Ⅲで一番難しい分野は',
      answerOptions:[
        {answerText:'微分積分',isCorrect:true},
        {answerText:'ベクトル',isCorrect:false},
        {answerText:'2次曲線',isCorrect:false},
      ],
    },
    {
      questionText:'この中で難易度が低いプログラミング言語は',
      answerOptions:[
        {answerText:'React.js',isCorrect:false},
        {answerText:'Angular.js',isCorrect:false},
        {answerText:'HTML',isCorrect:true},
      ],
    },
    {
      questionText:'この中でYouTuberのゲーム実況者は',
      answerOptions:[
        {answerText:'Taiman',isCorrect:false},
        {answerText:'ガッチマン',isCorrect:true},
        {answerText:'ハクア',isCorrect:false},
      ],
    },
  ];

  const [currentQuestion,setCurrentQuestion] = useState(0);

  const handleAnswerButtonClick = (isCorrect) => {


    //ここから追加
    if(isCorrect === true){
      alert('正解です');
      setScore(score+1);
    }else{
      alert('不正解です');
    }
    //ここまで追加

    const nextQuestion = currentQuestion + 1;

    if(nextQuestion < questions.length){
      setCurrentQuestion(nextQuestion);
    }
  }

  return (

    <div className="App">
      <Answer
        handleAnswerButtonClick={handleAnswerButtonClick}
        questions={questions}
        currentQuestion={currentQuestion}
      />
    </div>
  );
}

export default App;

次は正解だった場合の数を保存します。

正解した問題数の保存

App.jsのコードを下記にします。

import React,{useState} from 'react';
import Answer from './components/Answer';

function App() {

  const questions =[
    {
      questionText:'数学Ⅲで一番難しい分野は',
      answerOptions:[
        {answerText:'微分積分',isCorrect:true},
        {answerText:'ベクトル',isCorrect:false},
        {answerText:'2次曲線',isCorrect:false},
      ],
    },
    {
      questionText:'この中で難易度が低いプログラミング言語は',
      answerOptions:[
        {answerText:'React.js',isCorrect:false},
        {answerText:'Angular.js',isCorrect:false},
        {answerText:'HTML',isCorrect:true},
      ],
    },
    {
      questionText:'この中でYouTuberのゲーム実況者は',
      answerOptions:[
        {answerText:'Taiman',isCorrect:false},
        {answerText:'ガッチマン',isCorrect:true},
        {answerText:'ハクア',isCorrect:false},
      ],
    },
  ];

  const [currentQuestion,setCurrentQuestion] = useState(0);

  const [score,setScore] = useState(0);          //この行を追加

  const handleAnswerButtonClick = (isCorrect) => {

    if(isCorrect === true){
      alert('正解です');
    }else{
      alert('不正解です');
    }

    const nextQuestion = currentQuestion + 1;

    if(nextQuestion < questions.length){
      setCurrentQuestion(nextQuestion);
    }
  }

  return (

    <div className="App">
      <Answer
        handleAnswerButtonClick={handleAnswerButtonClick}
        questions={questions}
        currentQuestion={currentQuestion}
      />
    </div>
  );
}

export default App;

最後は問題を全部答えた後の得点の表示ですが三項演算子を使います。

最終的な得点の表示

App.jsのコードを下記にします。

import React,{useState} from 'react';
import Answer from './components/Answer';

function App() {

  const questions =[
    {
      questionText:'数学Ⅲで一番難しい分野は',
      answerOptions:[
        {answerText:'微分積分',isCorrect:true},
        {answerText:'ベクトル',isCorrect:false},
        {answerText:'2次曲線',isCorrect:false},
      ],
    },
    {
      questionText:'この中で難易度が低いプログラミング言語は',
      answerOptions:[
        {answerText:'React.js',isCorrect:false},
        {answerText:'Angular.js',isCorrect:false},
        {answerText:'HTML',isCorrect:true},
      ],
    },
    {
      questionText:'この中でYouTuberのゲーム実況者は',
      answerOptions:[
        {answerText:'Taiman',isCorrect:false},
        {answerText:'ガッチマン',isCorrect:true},
        {answerText:'ハクア',isCorrect:false},
      ],
    },
  ];

  const [currentQuestion,setCurrentQuestion] = useState(0);

  const [showScore,setShowScore] = useState(false);         //この行を追加

  const [score,setScore] = useState(0);

  const handleAnswerButtonClick = (isCorrect) => {

    if(isCorrect === true){
      alert('正解です');
      setScore(score+1);
    }else{
      alert('不正解です');
    }

    const nextQuestion = currentQuestion + 1;

    if(nextQuestion < questions.length){
      setCurrentQuestion(nextQuestion);
    }
    
    
    //ここから追加
    else{ 
      setShowScore(true);  

    }
    //ここまで追加
  }

  return (

    <div className="App">
    
      //ここから修正
      {
        showScore ? (
          <p>お疲れ様でした!<br></br><span class="correct">3問中{score}</span>正解です</p>
        )
        :
        (
          <Answer
            handleAnswerButtonClick={handleAnswerButtonClick}
            questions={questions}
            currentQuestion={currentQuestion}
          />
        )
      }
      //ここまで修正
    
    </div>
  );
}

export default App;

これで完成です。

最後にApp.jsとAnswer.jsxの完成系のコードを載せます。(index.cssは変更していないので載せません)

完成系のコード

下記がApp.jsです。


import React,{useState} from 'react';
import Answer from './components/Answer';

function App() {

  const questions =[
    {
      questionText:'数学Ⅲで一番難しい分野は',
      answerOptions:[
        {answerText:'微分積分',isCorrect:true},
        {answerText:'ベクトル',isCorrect:false},
        {answerText:'2次曲線',isCorrect:false},
      ],
    },
    {
      questionText:'この中で難易度が低いプログラミング言語は',
      answerOptions:[
        {answerText:'React.js',isCorrect:false},
        {answerText:'Angular.js',isCorrect:false},
        {answerText:'HTML',isCorrect:true},
      ],
    },
    {
      questionText:'この中でYouTuberのゲーム実況者は',
      answerOptions:[
        {answerText:'Taiman',isCorrect:false},
        {answerText:'ガッチマン',isCorrect:true},
        {answerText:'ハクア',isCorrect:false},
      ],
    },
  ];

  const [currentQuestion,setCurrentQuestion] = useState(0);

  const [showScore,setShowScore] = useState(false);

  const [score,setScore] = useState(0);

  const handleAnswerButtonClick = (isCorrect) => {

    if(isCorrect === true){
      alert('正解です');
      setScore(score+1);
    }else{
      alert('不正解です');
    }

    const nextQuestion = currentQuestion + 1;

    if(nextQuestion < questions.length){
      setCurrentQuestion(nextQuestion);
    }else{
      setShowScore(true);

    }
  }

  return (

    <div className="App">
      {
        showScore ? (
          <p>お疲れ様でした!<br></br><span class="correct">3問中{score}</span>正解です</p>
        )
        :
        (
          <Answer
            handleAnswerButtonClick={handleAnswerButtonClick}
            questions={questions}
            currentQuestion={currentQuestion}
          />
        )
      }
    </div>
  );
}

export default App;

下記がAnswer.jsxです。

import React from 'react';

    const Answer = ({handleAnswerButtonClick,questions,currentQuestion}) =>{
        return(
            <>
                <h1>3択クイズ</h1>
                <h2><span>{currentQuestion+1}</span><br></br>問題:{questions[currentQuestion].questionText}</h2>
                <ul> 
                {
                    questions[currentQuestion].answerOptions.map((answerOption,key)=>(
                        <li 
                            key={key}
                            onClick={()=>handleAnswerButtonClick(answerOption.isCorrect)}>{answerOption.answerText}
                        </li>)
                    )
                }
                </ul>
            </>
        );
    };

export default Answer;