共计 11443 个字符,预计需要花费 29 分钟才能阅读完成。
我的项目演示地址
我的项目演示地址
我的项目源码
我的项目源码
视频教程
视频教程
我的项目代码构造
前言
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
:
将其中的
<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>
)}
...
</>
);
};