React实战教程之从零开始手把手教你使用 React 最新特性Hooks API 打造一款计算机知识测验App

前言

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接口返回的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 && (
        <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>
(0)

相关推荐

  • React+electron项目搭建 打包

    一.搭建react+electron项目 1.创建一个react项目 create-react-app my-app cd my-app npm start 看下页面是否打开,是否运行正确. 注意:如 ...

  • react & dva 去掉地址栏的#号

    去掉地址栏的#号 使用react 配合dva 搭建的项目,项目运行之后,默认的地址栏是会有一个#号的,很烦,不想见到它,处理方式如下: 下载插件:history cnpm i history --sa ...

  • Ref实现导航滚动定位

    摘要 在开发项目中时常有点击跳转滚动到锚点的需求,最简单的锚点定位就是给一个a标签,a标签的href = '#锚点',然后给需要跳转的锚点一个id = '锚点'.参考最简单的锚点跳转实现方式,在Rea ...

  • hooks 与 animejs

    hooks 与 animejs 本文写于 2020 年 1 月 13 日 animejs 是现如今非常不错的一个 js 动画库.我们将其与 React Hooks 融合,使它更方便的在 React 中 ...

  • 简单的 useState 实现

    简单的 useState 实现 本文写于 2020 年 10 月 21 日 以下是一段非常简单的 React 代码: const App = () => { const [n, setN] = ...

  • 《深入浅出React和Redux》(2) - Redux

    ### Redux是Flux理念的一种实现. 关于Flux理念可以通过类比MVC模式来做简单理解. MVC模式中,用户请求先到达Controller,由Controller调用Model获得数据,然后 ...

  • 使用开源微前端框架 Luigi 创建一个基于微前端架构的工程

    官网地址 微前端通常被称为"前端微服务". 它们允许您将大型单体前端分解为独立的.可扩展的.可以协同工作的独立部分. 微前端架构对于复杂的产品或拥有众多团队的公司尤其有用,可以帮助 ...

  • Rematch的深入学习与实战应用(一),简易数字计数器

    摘要 近期在优化团队代码,发现Redux重复使用的代码过多. 经过调研发现了Rematch库:Redux是一个出色的状态管理工具,并且有着健全的中间件生态以及出色的开发工具:Rematch是没有boi ...

  • react+taro-JYwebApp模板集成方案项目搭建【1】

    本章节: 讲述基于react+taro-JYwebApp,项目的基础搭建.本主题讲述了react+taro-JYwebApp .webapp模板-集成方案,从零到一的手写搭建全过程. 该项目不仅是一个 ...

  • Web 性能优化: 使用 Webpack 分离数据的正确方法

    WEB前端开发社区 昨天 制定向用户提供文件的最佳方式可能是一项棘手的工作. 有很多不同的场景,不同的技术,不同的术语. 在这篇文章中,我希望给你所有你需要的东西,这样你就可以: 了解哪种文件分割策略 ...

  • webpack 5 带来的全新改变

    安装 webpack-dev-server npm install webpack-dev-server -D 在 webpack配置文件中配置服务: devServer:{ port: 8080, ...

  • 试试激动人心的 WebView2

    WebView2 实在诱人,最新的 Edge(Chromium) 性能强悍,而且所有使用 WebView2  的应用可以共用一个运行时,Windows 11 已经自带 WebView2 ,就连 Off ...

  • React Native之导出

    React Native之导出