项目演示地址

项目演示地址

项目源码

项目源码

视频教程

视频教程

项目代码结构

前言

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-app
npm install --g yarn

(4)在你想创建项目的目录下 例如 D:/project/ 打开cmd命令 输入

create-react-app react-exam

去使用creat-react-app命令创建名字是react-exam的项目

安装完成后,移至新创建的目录并启动项目

cd react-exam
yarn 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
将其中的

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

修改为

        <aclassName="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 (<><TogglesetQuestionData={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 /><FormonSubmit={(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接口返回的json数据格式为

{"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 && (<TogglesetIndex={setIndex}setQuestionData={setQuestionData}setToggleView={setToggleView}/>)}{!toggleView &&(<Jumbotron><QuestionHeadersetToggleView={setToggleView}/><Question question={questions[index]} /></Jumbotron>)}</>);

使用index控制题目索引

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

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

export const Toggle = ({setQuestionData,setToggleView,setIndex,
}) => {...return (<><ToggleHeader /><FormonSubmit={(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 valuesvar 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>...<AnswersetIndex={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) => (<Answertext={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 />) : (...<AnswerListanswers={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>...<AnswerListanswers={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) => (<Answertext={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 && (<TogglesetIndex={setIndex}setQuestionData={setQuestionData}setToggleView={setToggleView}setLoading={setLoading}/>)}{!toggleView &&(isLoading ? (<LoadingSpin />) :(<Jumbotron><QuestionHeadersetToggleView={setToggleView}/><Question question={questions[index]} /><AnswerListanswers={answers[index]}index={index}setIndex={setIndex}setResult={setResult}/></Jumbotron>))}<GameOverright={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 answersconst 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 (<Buttoncolor="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><ScoreHeadersetToggleView={setToggleView}setGameOver={setGameOver}/><ScoreBoard right={right} finalScore={right / index} /></Jumbotron>)}...</>);
};

从0开始,手把手教你使用React开发答题App相关推荐

  1. clion浏览linux代码,手把手教你使用 Clion 开发 Linux C++ 项目

    手把手教你使用 Clion 开发 Linux C++ 项目 关于CLion CLion是一款专为开发C及C++所设计的跨平台IDE.它是以IntelliJ为基础设计的,包含了许多智能功能来提高开发人员 ...

  2. python numpy安装教程_手把手教你搭建机器学习开发环境—Python与NumPy的超简安装教程...

    手把手教你搭建机器学习开发环境Python语言是机器学习的基础,所以,想要入门机器学习,配置好Python的开发环境是第一步.本文就手把手的教你配置好基于Python的机器学习开发环境.超简单!第一步 ...

  3. 手把手教你使用 Clion 开发 Linux C++ 项目

    手把手教你使用 Clion 开发 Linux C++ 项目 关于CLion CLion是一款专为开发C及C++所设计的跨平台IDE.它是以IntelliJ为基础设计的,包含了许多智能功能来提高开发人员 ...

  4. 【嵌入式开发】手把手教你4418/6818开发板屏幕修改 本文转自迅为: http://www.topeetboard.com 开发平台:iTOP-4418/6818开发板 44186818屏幕

    [嵌入式开发]手把手教你4418/6818开发板屏幕修改 本文转自迅为: http://www.topeetboard.com 开发平台:iTOP-4418/6818开发板 4418&6818 ...

  5. 译文1 手把手教你用cocos2d开发iphone游戏

    手把手教你用cocos2d开发iphone游戏-译文1 (2011-07-07 16:37:00) Learning Cocos2d – A Hands On Guide to Building iO ...

  6. 手把手教你搭建Linux开发环境(VMware+Ubuntu)(一)——安装VMware虚拟机和Ubuntu

    前言 近期好多小伙伴都开始学习Linux内核了,那么如何搭建一个Linux运行环境,变成Linux内核初学者的拦路虎,今天我就一步步详细解说一下,如何使用虚拟机VMware安装Ubuntu,跟我一起开 ...

  7. 手把手教你搭建Linux开发环境(VMware+Ubuntu)(二)——安装VMwareTools并设置共享文件夹

    刚刚装好了Ubuntu,安装VMware Tools会让我们有更好的体验,那么为什么要安装VMware Tools?该如何安装呢?本篇博客将手把手教你安装VMware Tools,并设置共享文件夹. ...

  8. 嵌入式LINUX搭建arm环境,手把手教你嵌入式ARM开发环境搭建

    原标题:手把手教你嵌入式ARM开发环境搭建 1. 安装,配置,启动FTP服务 · 安装FTP: sudo apt-get install vsftpd · 修改vsftpd的配置文件/etc/vsft ...

  9. 【手把手教你薅羊毛(新闻APP)】

    [手把手教你薅羊毛(新闻APP)] 这几天因为有点空闲,花时间研究了下最近很火的新闻APP邀请赚钱的套路,现分享给大家,羊毛薅起来哈. 没空的直接拉到最后看结论,有空的可以看看下面的介绍 今日头条极速 ...

最新文章

  1. Django用来作为爬虫框架浅谈
  2. hdu 1753大小数相加
  3. Python3.X新特性之print和exec
  4. oracle开放查询表权限_oracle 查询当前用户的表和其他用户的表
  5. IDEA 2020.3.2控制台中文乱码分享(亲测二、下图中控制台的编码改为UTF-8)
  6. leetcode-260.只出现一次的数字 III 解法
  7. php特殊符号写入excel_PHP:使用PEAR写入excel文件
  8. linux配置iscsi无账号密码,linux配置ISCSI服务器的方法
  9. oracle怎么变为整数,如何在Oracle 11g SQL中为char添加整数?(How to add integers to char in Oracle 11g SQL?)...
  10. linux网络编程--服务器模型(epoll/select/poll)
  11. 微信小程序云开发教程-微信小程序的API入门-获取用户身份信息系列API
  12. 主流PCB画图软件的对比区别(AD、Pads、Allegro)
  13. 昆仑通态触摸屏保存历史曲线_昆仑通态历史曲线如何组态呢?
  14. html静态页面作业——绿色特产商城购物网(11页) HTML+CSS+JavaScript 网页设计作业,网页制作作业, 学生网页作业, 网页作业成品, 网页作业模板
  15. java oracle sqlldr,oracle sqlldr
  16. 每年的节假日数据处理
  17. 博客变味:从一方净土沦落为企业枪手
  18. MVP从入门到...
  19. 吉林大学软件学院c++优秀课设资源讲解
  20. 水煮旅途之“天山夜话”

热门文章

  1. python的短路计算
  2. 不小心手误删了照片还能恢复吗
  3. 计算机应用基础考试高起专,计算机应用基础试题(高起专)
  4. Linux 下载安装VSCode 使用编程输出当前时间
  5. 采用FPGA IP实现DDR的读写控制的设计与验证
  6. 图灵机器人( 智能回复微信)及(智能聊天)
  7. Flutter之实现可折叠的列表
  8. python web 服务器实时监控 websocket_python实现websocket服务器,可以在web实时显示远程服务器日志...
  9. 一个开源的商城:学习Cloud必备
  10. 《Mall商城的设计与实现》软件工程综合实践 课程设计