React简单实现答题程序


前言

今下午花了两三个小时,用最近一周学的东西做了一个很简单多选的答题程序,也算对前段时间学的一些的东西的总结,代码部分可能会多少有点冗余,毕竟是个粗工程,由于没有后端数据,我简单的用浏览器的l本地存储localStorage来实现数据的存取,代码编写用的是函数组件,UI框架简单用了一下Antd,css样式使用sass进行预处理。


一、简单概念

1.函数组件

React 的函数组件是 React 组件的另一种定义方式,两种方式都可以用于定义组件,但是相比于类组件,函数组件要更简单好用些函数组件的创建方式就是定义一个函数,这个函数返回一个React组件。 我们可以通过普通函数的方法和箭头函数的方法创建一个函数组件。

2.Hook

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得我们不使用 class 也能使用 React。

import React, { useState } from 'react';

function Example() {
  // 声明一个叫 "count" 的 state 变量
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useState(0)是一个可以返回一个数组的函数,该数组有两个参数,一个是我们创建的state变量,一个是该state变量的set方法,本身就是一个函数。

二、一个小项目

1.介绍

这个简单的项目是一个简单的答题程序,通过完成一些题目(多选题)来计算最后的得分,主要熟悉一下表单的操作,以及组件之前的传值操作。

2.Schema设计

const defalutschema = {
  id: 1,
  desc: '前端面试题',
  outScore: 80,
  totalScore: 0,
  question: [
    {
      id: 1,
      desc: 'let,const,var的区别?',
      the_score: 0,
      options: [
        {
          label: '变量提升:let,const定义的变量不会出现变量提升,而var会',
          value: '变量提升:let,const定义的变量不会出现变量提升,而var会',
          score: 10,
          selected: false
        },
        {
          label: '重复声明:同一作用域下let,const声明的变量不允许重复声明,而var可以',
          value: '重复声明:同一作用域下let,const声明的变量不允许重复声明,而var可以',
          score: 10,
          selected: false
        },
        {
          label: '暂时性死区:let,const声明的变量不能在声明之前使用,而var可以',
          value: '暂时性死区:let,const声明的变量不能在声明之前使用,而var可以',
          score: 20,
          selected: false
        },
        {
          label: '错误答案',
          value: '错误答案',
          score: 0,
          selected: false
        }
      ],
      selected: [

      ]
    }, {
      id: 2,
      desc: 'JS延迟加载的方式',
      the_score: 0,
      options: [
        {
          label: '把JS放在页面的最底部',
          value: '把JS放在页面的最底部',
          score: 10,
          selected: false
        },
        {
          label: 'script标签的defer属性:脚本会立即下载但延迟到整个页面加载完毕再执行。该属性对于内联脚本无作用 (即没有 「src」 属性的脚本)。',
          value: 'script标签的defer属性:脚本会立即下载但延迟到整个页面加载完毕再执行。该属性对于内联脚本无作用 (即没有 「src」 属性的脚本)。',
          score: 10,
          selected: false
        },
        {
          label: 'Async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行,标记为async的脚本并不保证按照指定他们的先后顺序执行,该属性对于内联脚本无作用 (即没有 「src」 属性的脚本)。',
          value: 'Async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行,标记为async的脚本并不保证按照指定他们的先后顺序执行,该属性对于内联脚本无作用 (即没有 「src」 属性的脚本)。',
          score: 10,
          selected: false
        },
        {
          label: '动态创建script标签,监听dom加载完毕再引入js文件',
          value: '动态创建script标签,监听dom加载完毕再引入js文件',
          score: 10,
          selected: false
        }
      ],
      selected: [

      ]
    }

  ]
}

3.App组件

App.js

import Question from "../Question/question";
import 'antd/dist/antd.css';
import { createRef, useMemo, useState } from "react";
import { Button } from "antd";
import styles from './Style.module.scss';
const defalutschema = {.......} //见上方schema的设计
  
window.localStorage.schema = JSON.stringify(defalutschema);

let a_schema;
try {
  a_schema = JSON.parse(window.localStorage.schema);
  console.log(1, a_schema);
} catch { };


let refs = [];
const App = (props) => {
  const [schema, setSchema] = useState(a_schema);
  const [finish, setFinish] = useState(false);
  const [finishScore, setFinishScore] = useState(0);
  const [disabled, setDisabled] = useState(false);
  let { question, desc, outScore } = schema;
  let totalScore;
  let newSchema = {
    id: 1,
    totalScore: 0,
    question: [],
  };
  const handleSave = () => {
    setFinish(true);
    totalScore = 0;
    schema.question.forEach((item, index) => {
      const { getSchema } = refs[index].current;
      newSchema.question.push(getSchema());
      if (getSchema().the_score) {
        totalScore += getSchema().the_score;
      }

    });
    newSchema.totalScore = totalScore;
    window.localStorage.schema = JSON.stringify(newSchema);
    setDisabled(true);
    setFinishScore(newSchema.totalScore);
    //alert(`你本次得分${newSchema.totalScore}!`)
  }
  useMemo(() => {
    refs = question.map(item => createRef());
  }, [question])


  return (
    <div className={styles.AppWrapper}>
      <p className={styles.desc}>{desc} (满分{outScore})</p>
      {finish ? <div className={styles.totalScore}>{finishScore}</div> : <div className={styles.totalScore}></div>}
      {
        schema.question.map((item, index) => {
          return (
            <div key={item.id} className={styles.question}>
              <p className={styles.title}>{item.id}{item.desc}</p>
              <Question finish={finish} item={item} ref={refs[index]}></Question>
            </div>)
        })
      }
      <Button type="primary" className={styles.Submitbtn} onClick={() => { handleSave() }} disabled={disabled}>交卷</Button>
    </div>
  )
}

export default App;


Style.module.scss

.AppWrapper{
    background-color: aliceblue;
    width: 400px;
    margin: 20px;
    padding: 10px;
    position: relative;
    .totalScore{
        position: absolute;
        right: -10px;
        top: -30px;
        font-size: 50px;
        font-weight: bold;
        color: red;
        font-family: 'Times New Roman', Times, serif;
    }
    .desc{
        font-size: 20px;
        font-weight: bold;
    }
    .question{
        width: 100%;
        border: 1px solid #333;
        padding: 10px;
        font-size: 20px;
        border-radius: 10px;
        margin-top: 20px;

        .title{
            width: 100%;
            height: 20px;
            line-height: 20px;
        }
    }
    .Submitbtn{
        margin-top: 20px;
    }
}

3.Question组件

question.js

import { Checkbox } from 'antd';
import React, { forwardRef, useImperativeHandle, useState } from 'react';
import styles from './style.module.scss';

let score = 0;
const char = ['A', 'B', 'C', 'D', 'E', 'F'];


const Question = (props, ref) => {
    const { finish } = props;
    const { id, desc, options } = props.item;
    const [newSchema, setNewSchema] = useState({});
    const [yanz, setYanz] = useState([0, 0, 0, 0]);
    useImperativeHandle(ref, () => {
        return {
            getSchema: () => {
                return newSchema
            }
        }
    })
    const getTag = (index, finish, yanz) => {
        console.log(yanz);
        if (yanz[index] === 1 && finish) {
            return (<div className={styles.duicuo}></div>)
        } else if (yanz[index] === 2 && finish) {
            return (<div className={styles.duicuo}></div>)
        } else {
            return;
        }
    }
    const onChange = (item) => {
        item.selected = !item.selected;
        let flag = 1;
        score = 0;
        let selectedArr = [];
        setYanz([0, 0, 0, 0])
        options.forEach((item, index) => {
            if (item.selected && item.score !== 0) {
                score += parseInt(item.score);
                selectedArr.push(item);
                const newYanz = yanz;
                newYanz.splice(index, 1, 2);
                setYanz(newYanz)
                console.log(122, yanz);
            } else if ((item.selected && item.score == 0)) {
                flag = 0;
                const cuoYanz = yanz;
                cuoYanz.splice(index, 1, 1);
                setYanz(cuoYanz)
            }
        })
        if (!flag) {
            score = 0;
        }
        const schema = {
            id,
            desc,
            the_score: score,
            options: options,
            selected: selectedArr
        }
        setNewSchema(schema);
    }

    return (

        <div className=''>
            {
                options.map((item, index) => (
                    <div className={styles.wrapper}>
                        {
                            getTag(index, finish, yanz)
                        }

                        <Checkbox key={index} defaultChecked={item.selected} onChange={() => { onChange(item) }}>{char[index]}{item.label}</Checkbox>
                        <br />

                    </div>
                ))
            }
        </div>
    )
};

export default forwardRef(Question);

style.module.scss

.wrapper{
    position: relative;
    .duicuo{
        position: absolute;
        top:14px;
        left: 10px;
        z-index: 9999;
        color: red;
        font-weight: bold;
    }
}

3.最终效果

在这里插入图片描述

在这里插入图片描述


总结

最近还是学了一点东西,但是感觉熟练度还是欠佳,还在写一个个人博客的小网站,用的东西还是挺多的,这篇博客写完就得开始动手赶作业了,好久没写作业了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值