我的项目演示地址

我的项目演示地址

我的项目源码

我的项目源码

视频教程

视频教程

我的项目代码构造

前言

React 框架的优雅显而易见,组件化的编程思维使得React框架开发的我的项目代码简洁,易懂,但晚期 React 类组件的写法略显繁琐。React Hooks 是 React 16.8 公布以来最吸引人的个性之一,她简化了原有代码的编写,是将来 React 利用的支流写法。

本文通过一个实战小我的项目,手把手从零开始率领大家疾速入门React Hooks。本我的项目线上演示地址:

在本我的项目中,会用到以下知识点:

  • React 组件化设计思维
  • React State 和 Props
  • React 函数式组件的应用
  • React Hooks useState 的应用
  • React Hooks useEffect 的应用
  • React 应用 Axios 申请近程接口获取问题及答案
  • React 应用Bootstrap丑化界面

Hello React

(1)装置node.js 官网链接

(2)装置vscode 官网链接

(3)装置 creat-react-app 性能组件,该组件能够用来初始化一个我的项目, 即依照肯定的目录构造,生成一个新我的项目。
关上cmd 窗口 输出:

npm install --g create-react-app npm install --g yarn

(-g 代表全局装置)

如果装置失败或较慢。须要换源,能够应用淘宝NPM镜像,设置办法为:
npm config set registry https://registry.npm.taobao.org

设置实现后,从新执行

npm install --g create-react-appnpm install --g yarn

(4)在你想创立我的项目的目录下 例如 D:/project/ 关上cmd命令 输出

create-react-app react-exam

去应用creat-react-app命令创立名字是react-exam的我的项目

装置实现后,移至新创建的目录并启动我的项目

cd react-examyarn start

一旦运行此命令,localhost:3000新的React应用程序将弹出一个新窗口。

我的项目目录构造

右键react-exam目录,应用vscode关上该目录。
react-exam我的项目目录中有一个/public和/src目录,以及node_modules,.gitignore,README.md,和package.json。

在目录/public中,重要文件是index.html,其中一行代码最重要

<div id="root"></div>

该div做为咱们整个利用的挂载点

/src目录将蕴含咱们所有的React代码。

要查看环境如何主动编译和更新您的React代码,请找到文件/src/App.js
将其中的

        <a          className="App-link"          href="https://reactjs.org"          target="_blank"          rel="noopener noreferrer"        >          Learn React        </a>

批改为

        <a          className="App-link"          href="https://reactjs.org"          target="_blank"          rel="noopener noreferrer"        >          和豆约翰 Learn React        </a>

保留文件后,您会留神到localhost:3000编译并刷新了新数据。

React-Exam我的项目实战

1. 首页制作

1.装置我的项目依赖,在package.json中增加:

  "dependencies": {    "@testing-library/jest-dom": "^4.2.4",    "@testing-library/react": "^9.3.2",    "@testing-library/user-event": "^7.1.2",    "react": "^16.13.1",    "react-dom": "^16.13.1",    "react-scripts": "3.4.1",    "axios": "^0.19.2",    "bootstrap": "^4.5.0",    "he": "^1.2.0",    "react-loading": "^2.0.3",    "reactstrap": "^8.4.1"  },

执行命令:

yarn install

批改index.js,导入bootstrap款式

import "bootstrap/dist/css/bootstrap.min.css";

批改App.css代码

html {  width: 80%;  margin-left: 10%;  margin-top: 2%;}.ansButton {  margin-right: 4%;  margin-top: 4%;}

批改App.js,引入Quiz组件

import React from 'react';import './App.css'import { Quiz } from './Exam/Quiz';function App() {  return (    <div className = 'layout'>    <Quiz></Quiz>    </div>  );}export default App;

在我的项目src目录下新增Exam目录,Exam目录中新建Quiz.js

Quiz组件的定义如下:
Quiz.js,引入开始页面组件Toggle。

import React, { useState } from "react";import { Toggle } from "./Toggle";export const Quiz = () => {  const [questionData, setQuestionData] = useState([]);  const questions = questionData.map(({ question }) => [question]);  const answers = questionData.map(({ incorrect_answers, correct_answer }) =>    [correct_answer, incorrect_answers].flat()  );  return (    <>      <Toggle        setQuestionData={setQuestionData}      />    </>  );};

Toggle.js,点击开始按钮,通过axios拜访近程接口,取得题目及答案。

import React from "react";import axios from "axios";import ToggleHeader from "./ToggleHeader";import {  Button,  Form,} from "reactstrap";export const Toggle = ({  setQuestionData,}) => {  const getData = async () => {    try {      const incomingData = await axios.get(        `https://opentdb.com/api.php?amount=10&category=18&difficulty=easy&type=multiple`      );      setQuestionData(incomingData.data.results);    } catch (err) {      console.error(err);    }  };  return (    <>      <ToggleHeader />      <Form        onSubmit={(e) => {          e.preventDefault();          getData();        }}      >        <Button color="primary">开始</Button>      </Form>    </>  );};

ToggleHeader.js

import React from "react";import { Jumbotron, Container} from "reactstrap";export default function ToggleHeader() {  return (    <Jumbotron fluid>      <Container fluid>        <h1 className="display-4">计算机常识小测验</h1>      </Container>    </Jumbotron>  );}

https://opentdb.com/api.php接...

{    "response_code": 0,    "results": [{        "category": "Science: Computers",        "type": "multiple",        "difficulty": "easy",        "question": "The numbering system with a radix of 16 is more commonly referred to as ",        "correct_answer": "Hexidecimal",        "incorrect_answers": ["Binary", "Duodecimal", "Octal"]    }, {        "category": "Science: Computers",        "type": "multiple",        "difficulty": "easy",        "question": "This mobile OS held the largest market share in 2012.",        "correct_answer": "iOS",        "incorrect_answers": ["Android", "BlackBerry", "Symbian"]    }, {        "category": "Science: Computers",        "type": "multiple",        "difficulty": "easy",        "question": "How many values can a single byte represent?",        "correct_answer": "256",        "incorrect_answers": ["8", "1", "1024"]    }, {        "category": "Science: Computers",        "type": "multiple",        "difficulty": "easy",        "question": "In computing, what does MIDI stand for?",        "correct_answer": "Musical Instrument Digital Interface",        "incorrect_answers": ["Musical Interface of Digital Instruments", "Modular Interface of Digital Instruments", "Musical Instrument Data Interface"]    }, {        "category": "Science: Computers",        "type": "multiple",        "difficulty": "easy",        "question": "In computing, what does LAN stand for?",        "correct_answer": "Local Area Network",        "incorrect_answers": ["Long Antenna Node", "Light Access Node", "Land Address Navigation"]    }]}

程序运行成果:

以后我的项目目录构造为:

2. 问题展现页面

Quiz.js,新增toggleView变量用来切换视图。

  const [toggleView, setToggleView] = useState(true);

Quiz.js,其中Question和QuestionHeader 组件,参见前面。

import { Question } from "./Question";import { Jumbotron } from "reactstrap";import QuestionHeader from "./QuestionHeader";...export const Quiz = () => {  var [index, setIndex] = useState(0);  const [questionData, setQuestionData] = useState([]);... return (    <>      {toggleView && (        <Toggle          setIndex={setIndex}          setQuestionData={setQuestionData}          setToggleView={setToggleView}        />      )}       {!toggleView &&        (          <Jumbotron>            <QuestionHeader              setToggleView={setToggleView}            />            <Question question={questions[index]} />          </Jumbotron>        )}    </>  );

应用index管制题目索引

var [index, setIndex] = useState(0);

批改Toggle.js
获取完近程数据,通过setToggleView(false);切换视图。

export const Toggle = ({  setQuestionData,  setToggleView,  setIndex,}) => {  ...  return (    <>      <ToggleHeader />      <Form        onSubmit={(e) => {          e.preventDefault();          getData();          setToggleView(false);          setIndex(0);        }}      >        <Button color="primary">开始</Button>      </Form>    </>  );};

QuestionHeader.js代码:
同样的,点击 返回首页按钮 setToggleView(true),切换视图。

import React from "react";import { Button } from "reactstrap";export default function QuestionHeader({ setToggleView, category }) {  return (    <>      <Button color="link" onClick={() => setToggleView(true)}>        返回首页      </Button>    </>  );}

Question.js代码
承受父组件传过来的question对象,并显示。
其中he.decode是对字符串中的特殊字符进行本义。

import React from "react";import he from "he";export const Question = ({ question }) => {  // he is a oddly named library that decodes html into string values  var decode = he.decode(String(question));  return (    <div>      <hr className="my-2" />      <h1 className="display-5">        {decode}      </h1>      <hr className="my-2" />      <br />    </div>  );};

程序运行成果:
首页

点击开始后,显示问题:

以后我的项目目录构造为:

3. 加载期待动画

新增LoadingSpin.js

import React from "react";import { Spinner } from "reactstrap";export default function LoadingSpin() {  return (    <>      <Spinner type="grow" color="primary" />      <Spinner type="grow" color="secondary" />      <Spinner type="grow" color="success" />      <Spinner type="grow" color="danger" />    </>  );}

批改Quiz.js

import LoadingSpin from "./LoadingSpin";export const Quiz = () => {  const [isLoading, setLoading] = useState(false);  return (    <>      {toggleView && (        <Toggle          ...          setLoading={setLoading}        />      )}      {!toggleView &&        (isLoading ? (          <LoadingSpin />        ) :         (          ...        ))}    </>  );};

批改Toggle.js

export const Toggle = ({...  setLoading,}) => {  const getData = async () => {    try {      setLoading(true);      const incomingData = await axios.get(        `https://opentdb.com/api.php?amount=10&category=18&difficulty=easy&type=multiple`      );      setQuestionData(incomingData.data.results);      setLoading(false);    } catch (err) {      console.error(err);    }  }; ...};

运行成果:

目前代码构造:

4. 实现下一题性能

新增Answer.js,用户点击下一题按钮,批改index,触发主界面刷新,显示下一题:

import React from "react";import { Button } from "reactstrap";export const Answer = ({ setIndex, index }) => {  function answerResult() {    setIndex(index + 1);  }  return (    <Button className="ansButton" onClick={answerResult}>      下一题    </Button>  );};

批改Quiz.js,增加Answer组件:

import { Answer } from "./Answer";... {!toggleView &&        (isLoading ? (          <LoadingSpin />        ) :         (          <Jumbotron>            ...            <Answer            setIndex={setIndex}            index={index}            />          </Jumbotron>        ))}

运行成果:

点击下一题:


5. 实现选项展现

新增AnswerList.js。
通过属性answers传进来的选项列表,须要被打乱程序(shuffle )

import React from "react";import { Answer } from "./Answer";export const AnswerList = ({ answers, index, setIndex }) => {  if (answers) var correctAns = answers[0];  const shuffle = (array) => {    return array.sort(() => Math.random() - 0.5);  };  const arrayCheck = (arg) => {    return Array.isArray(arg) ? arg : [];  };  return (    <>      {shuffle(arrayCheck(answers)).map((text,ind) => (        <Answer          text={text}          correct={correctAns}          setIndex={setIndex}          index={index}          key={ind}        />      ))}    </>  );};

批改Answer.js

import React from "react";import he from "he";import { Button } from "reactstrap";export const Answer = ({ text, correct, setIndex, index }) => {  function answerResult() {    setIndex(index + 1);  }  var decode = he.decode(String(text));  return (    <Button className="ansButton" onClick={answerResult}>      {decode}    </Button>  );};

批改Quiz.js

// import { Answer } from "./Answer";import { AnswerList } from "./AnswerList";export const Quiz = () => {...  return (    <>      ...      {!toggleView &&        (isLoading ? (          <LoadingSpin />        ) :         (        ...            <AnswerList              answers={answers[index]}              index={index}              setIndex={setIndex}            />          </Jumbotron>        ))}    </>  );};

运行成果:

我的项目构造:

6. 记录用户问题

批改quiz.js,增加setResult,并传递给AnswerList

export const Quiz = () => {   var [result, setResult] = useState(null);...  return (    <>    ...      {!toggleView &&        (isLoading ? (          <LoadingSpin />        ) :         (          <Jumbotron>          ...            <AnswerList              answers={answers[index]}              index={index}              setIndex={setIndex}              setResult={setResult}            />          </Jumbotron>        ))}    </>  );};

批改AnswerList.js,传递setResult

import React from "react";import { Answer } from "./Answer";export const AnswerList = ({ answers, index,setResult, setIndex }) => {...  return (    <>      {shuffle(arrayCheck(answers)).map((text,ind) => (        <Answer          text={text}          correct={correctAns}          setIndex={setIndex}          setResult={setResult}          index={index}          key={ind}        />      ))}    </>  );};

批改Answer.js,用户点击选项,回调setResult,告诉Quiz组件,本次抉择是对是错。

import React from "react";import { Button } from "reactstrap";import he from 'he'export const Answer = ({ text, correct, setResult,setIndex, index }) => {  function answerResult() {    setIndex(index + 1);    correct === text ? setResult(true) : setResult(false);  }  var decode = he.decode(String(text));  return (    <Button className="ansButton" onClick={answerResult}>      {decode}    </Button>  );};

批改Quiz.js,放一个暗藏的GameOver组件,每当index发生变化的时候,触发GameOver中的useEffect代码,累计用户答对题目的数目(setRight)

import GameOver from "./GameOver";export const Quiz = () => {  const [right, setRight] = useState(0);  const [gameIsOver, setGameOver] = useState(false);  return (   <>      {toggleView && (        <Toggle          setIndex={setIndex}          setQuestionData={setQuestionData}          setToggleView={setToggleView}          setLoading={setLoading}        />      )}      {!toggleView &&        (isLoading ? (          <LoadingSpin />        ) :          (            <Jumbotron>              <QuestionHeader                setToggleView={setToggleView}              />              <Question question={questions[index]} />              <AnswerList                answers={answers[index]}                index={index}                setIndex={setIndex}                setResult={setResult}              />            </Jumbotron>      ))}      <GameOver        right={right}        setRight={setRight}        quizLength={questions.length}        setGameOver={setGameOver}        result={result}        index={index}      />    </>  );};

新增GameOver.js组件,当index === quizLength && index时,setGameOver(true)设置游戏完结,显示用户得分。

import React, { useEffect } from "react";export default function GameOver({  right,  setRight,  setGameOver,  index,  quizLength,  result,}) {  useEffect(() => {    if (result === true) {      setRight(right + 1);    }     if (index === quizLength && index) {      setGameOver(true);    }  }, [index]);  return <div></div>;}

7. 游戏完结,展现用户得分

新增ScoreBoard.js

import React from "react";export const ScoreBoard = ({ finalScore, right }) => {  // if index === 0 then right === 0 --> this way when index is reset in toggle so is right answers  const scoreFormatted = score => {    if (score === 1) {      return 100;    } else if (score === 0) {      return 0;    } else {      return score.toFixed(2) * 100;    }  }  return (    <>      <>        <h1 className="display-4">Correct Answers: {right}</h1>        <hr className="my-2" />        <h1 className="display-4">          Final Score: %{scoreFormatted(finalScore)}        </h1>        <hr className="my-2" />      </>      <p>谢谢应用 </p>    </>  );};

ScoreHeader.js

import React from "react";import { Button } from "reactstrap";export default function ScoreHeader({ setGameOver, setToggleView }) {  return (    <Button      color="link"      onClick={() => {        setGameOver(false);        setToggleView(true);      }}    >      返回首页    </Button>  );}

批改Quiz.js,当gameIsOver 变量为true时,显示得分页面。

import { ScoreBoard } from "./ScoreBoard";import ScoreHeader from "./ScoreHeader";export const Quiz = () => {...  return (    <>       {!toggleView &&        !gameIsOver &&        (isLoading ? (          <LoadingSpin />        ) :         (          ...          ))}                  {gameIsOver && (        <Jumbotron>          <ScoreHeader            setToggleView={setToggleView}            setGameOver={setGameOver}          />          <ScoreBoard right={right} finalScore={right / index} />        </Jumbotron>      )}         ...    </>  );};