IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React 项目实战——使用React实现计算器 -> 正文阅读

[JavaScript知识库]React 项目实战——使用React实现计算器

React 项目——计算器


一、总体设计

采用 React 框架实现计算器的基础功能。

1、 搭建界面如下:
在这里插入图片描述
1.1 计算器主要功能组件设计:calculator组件

在这里插入图片描述
计算机功能实现主要有三个状态(当前操作数、上一个操作数、操作符)、五种操作(增加数字、回退数字、选择运算符、计算、清空)。
在这里插入图片描述

1.2 整体界面设计:
主要模块与组件Dom树如下,包含 NavbarContentHome、Calculator、Login、Register、404)组件。
在这里插入图片描述
Router 主要路径如下:
在这里插入图片描述

二、代码实现

1、各组件的建立

1.1 Navbar 组件

// navbar.jsx
import React, { Component } from 'react';
import { Link} from 'react-router-dom';

class Navbar extends Component {
    state = {  } 
    render() { 
        return (
            <nav className="navbar navbar-expand-lg navbar-light bg-light">
                <div className="container">
                    <Link className="navbar-brand" to="/"> Web </Link>
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                    </button>
                    <div className="collapse navbar-collapse" id="navbarText">
                        <ul className="navbar-nav me-auto mb-2 mb-lg-0">
                            <li className="nav-item">
                                <Link className="nav-link"  to="/home">首页</Link>
                            </li>
                            <li className="nav-item">
                                <Link className="nav-link" to="/calculator">计算器</Link>
                            </li>
                        </ul>
                        <ul className="navbar-nav">
                            <li className="nav-item">
                                <Link className="nav-link"  to="/login">登录</Link>
                            </li>   
                            <li className="nav-item">
                                <Link className="nav-link" to="/register">注册</Link>
                            </li>
                        </ul>
                    </div>
                </div>
            </nav>
        );
    }
}
 
export default Navbar;

1.2 ContentBase 组件

// contentBase.jsx
import React, { Component } from 'react';

class ContentBase extends Component {
    state = {  } 
    render() { 
        return (
            <div className="card" style={{marginTop: "20px", backgroundColor:"#F8F9FA"}}>
                <div className="card-body">
                    <h4 className="card-title">{this.props.children}</h4>
                    <h6 className="card-subtitle mb-2 text-muted">Card subtitle</h6>
                    <p className="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
                    <a href="/" className="card-link">Card link</a>
                    <a href="/" className="card-link">Another link</a>
                </div>
            </div>
        );
    }
}
 
export default ContentBase;

1.3 CalCulator 组件(内含多个Button组件)

// calculator.jsx
import React, { Component } from 'react';
import ContentBase from './contentbase';
import { connect } from 'react-redux';
import DigitButton from './calculator/digitButton';
import ACTIONS from './../../redux/action';
import OperatorButton from './calculator/operatorButton';

class Calculator extends Component {
    state = { };
    render() { 
        return (
            <ContentBase>
                Standard
                <div className="calculator">
                    <div className="output">
                        <div className="last-output">                           
                            {this.props.last}
                            {this.props.operator}
                        </div>
                        <div className="current-output">
                            {this.props.current}
                            </div>
                    </div>
                    
                    <button className='button-ac'  onClick={this.props.clear}>AC</button>
                    <button onClick={this.props.delete}>Del</button>
                    <OperatorButton operator="÷">÷</OperatorButton>

                    <DigitButton digit="7">7</DigitButton> 
                    <DigitButton digit="8">8</DigitButton>
                    <DigitButton digit="9">9</DigitButton>
                    <OperatorButton operator="×">×</OperatorButton>
                    
                    <DigitButton digit="4">4</DigitButton>
                    <DigitButton digit="5">5</DigitButton>
                    <DigitButton digit="6">6</DigitButton>
                    <OperatorButton operator="-">-</OperatorButton>

                    <DigitButton digit="1">1</DigitButton>
                    <DigitButton digit="2">2</DigitButton>
                    <DigitButton digit="3">3</DigitButton>
                    <OperatorButton operator="+">+</OperatorButton>

                    <DigitButton digit=".">.</DigitButton>
                    <DigitButton digit="0">0</DigitButton>
                    <button className='button-equal' onClick={this.props.evaluate}>=</button>
                </div>
            </ContentBase>
        );
    }
}


// 绑定 state :用于 output 的两个区域显示
const mapStateToProps = (state,props) => {
    return {
        current: state.currentOperand,
        last: state.lastOperand,
        operator: state.operator,
    }  
}

// 绑定 action : AC 键的清空 ,Del 的回退,= 的执行
const mapDispachToProps = {
    delete: ()=>{
        return {
            type: ACTIONS.DELETE_DIGIT,
        }
    },
    clear: ()=>{
        return {
            type: ACTIONS.CLEAR,
        }
    },
    evaluate: ()=>{
        return {
            type: ACTIONS.EVALUTE,
        }
    }
}

export default connect(mapStateToProps,mapDispachToProps)(Calculator);

1.4 Button 组件 (DigitButton组件 与 OperatorButton 组件)

// digitButton.jsx
import React, { Component } from 'react';
import ACTIONS from '../../../redux/action';
import { connect } from 'react-redux';

class DigitButton extends Component {
    state = {}
    render() {
        return (
            <button onClick={() => { this.props.add_digit(this.props.digit) }}> {this.props.digit} </button>
        );
    }
}


// 绑定 action:currentOperand 的 add_digit
const mapDispachToProps = {
    add_digit: (digit) => {
        return {
            type: ACTIONS.ADD_DIGIT,
            digit: digit,
        }
    }
};


export default connect(null, mapDispachToProps)(DigitButton);
// operatorButton.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import ACTIONS from './../../../redux/action';

class OperatorButton extends Component {
    state = {  } 
    render() { 
        return (
            <button onClick={ () => {this.props.choose_operator(this.props.operator)} }>{this.props.operator}</button>
        );
    }
}
 

// 绑定 action:choose_operator。(计算器使用中,清空可能有执行结果或者换运算符)
const mapDispachToProps = {
    choose_operator: (operator) => {
        return {
            type: ACTIONS.CHOOSE_OPERATOR,
            operator: operator,
        }
    }
};


export default connect(null,mapDispachToProps)(OperatorButton);
2、Route 实现路由

App 组件: 放入子组件并实现路由。
url:Route标签中的path对应 Navbar 组件的 Link 标签中的 to

import React, { Component } from 'react';
import Navbar from './navbar';
import { Routes, Route, Navigate } from 'react-router-dom';
import Home from './content/home';
import Calculator from './content/calculator';
import Login from './content/login';
import Register from './content/register';
import NotFound from './content/notFound';


class App extends Component {
    state = {  } 
    render() { 
        return (
            <React.Fragment>
                <Navbar></Navbar>
                <div className='container'>
                <Routes>
                    <Route path='/' element={<Home/>} />
                    <Route path='/home' element={<Home />} />
                    <Route path='/calculator' element={<Calculator />} />
                    <Route path='/login' element={<Login />} />
                    <Route path='/register' element={<Register />} />
                    <Route path='/404' element={<NotFound />} />
                    <Route path='*' element={ <Navigate replace to="404" />} />
                </Routes>
                </div>
            </React.Fragment>
        );
    }
}
 
export default App;
3、Redux 实现交互

3.1 构建 reducer 状态树(包括action.type 以及value)

操作类型(增加数字、删除数字、选择运算符、计算结果、清空)

// action.js
// 定义五种操作类型(增加数字、删除数字、选择运算符、计算结果、清空)
const ACTIONS = {
    ADD_DIGIT: "add_digit",
    DELETE_DIGIT: "delete_digit",
    CHOOSE_OPERATOR:"choose_operator",
    EVALUTE: "evalute",
    CLEAR: "clear"
};
export default ACTIONS;

上述将 action type 定为常量单独放在一个文件,有助于以后标识符名称的统一修改。

// reducer.js
import ACTIONS from "./action";

// 二、 计算结果函数
const evalute = (state) => {
    // 解构
    let {lastOperand,currentOperand,operator} = state;
    let last = parseFloat(lastOperand);
    let current = parseFloat(currentOperand);

    let res = '';
    switch(operator) {
        case '+': 
            res = last + current;break;
        case '-':
            res = last - current;break;
        case '×':
            res = last * current;break;
        case '÷':
            res = last / current;break;
        default:
    }
    return res.toString();
}

// 一、 构建状态函数
const reducer = (
    state = {
        currentOperand:"0",
        lastOperand:"",
        operator:"",
        // 标识之前是否按了 = 键, 是否需要重写
        overwrite:false, 
    },
    action
) => {
    switch(action.type){
        // 1. current 区 add_digit 操作
        case ACTIONS.ADD_DIGIT:
            if (state.overwrite === true){
                if(action.digit === '.')
                    return{
                     	// 首先按原有state展开                
                		// 局部值替换
                        ...state,
                        currentOperand: '0.',
                        overwrite:false
                    }
                return{
                    ...state,
                    currentOperand: action.digit,
                    overwrite:false
                }
            }
            if (state.currentOperand === '0' && action.digit === '0'){
                return state;
            }
            if (state.currentOperand === '0' && action.digit !== '.'){
                return {
                    ...state,
                    currentOperand: action.digit,
                }
            }
            if (state.currentOperand === '' && action.digit === '.'){
                return {
                    ...state,
                    currentOperand: '0.'
                }
            }      
            if (state.currentOperand.includes('.') && action.digit === '.'){
                return state;
            }
            return {
                // 首先按原有state展开
                ...state,
                // 局部值替换
                currentOperand: state.currentOperand + action.digit,
            }
        // 2. 回退操作
        case ACTIONS.DELETE_DIGIT:
            if (state.overwrite === true)
                return {
                    lastOperand: "0",
                    overwrite:"false"
                }
            if (state.currentOperand.length === 1)
                return {
                    ...state,
                    currentOperand: '0',
                }
            return {
                ...state,
                // 删除从 0 开始的 -1 个元素,即删除最后一个元素
                currentOperand:state.currentOperand.slice(0,-1)
            }
        // 3. 清空操作
        case ACTIONS.CLEAR:
            return {
                ...state,
                currentOperand: '0',
                lastOperand:'',
                operator:'',
            }
        // 4. 选择操作符并计算(选择操作符时,将current放入last)
        case ACTIONS.CHOOSE_OPERATOR:
            if(state.lastOperand === '')
                return {
                    ...state,
                    lastOperand: state.currentOperand, // 注意是原来的current
                    operator: action.operator,  // 注意是当前action传来的current
                    currentOperand: ''
                }
            if(state.lastOperand !== '' && state.currentOperand === '')
                return {
                    ...state,
                    operator: action.operator,
                }
            return {
                ...state,
                lastOperand: evalute(state),
                operator: action.operator,
                currentOperand: '',
            }
        // 5、 等号逻辑
        case ACTIONS.EVALUTE:
            if (state.currentOperand === "" ||
                state.operator === "" ||
                state.lastOperand === "")
                return state;
            return {
                ...state,
                currentOperand: evalute(state),
                lastOperand: "",
                operator:'',
                overwrite: true
            }
        default:
            return state;
    }
};

export default reducer;

3.2 构建 store 树

import { configureStore } from "@reduxjs/toolkit";
import reducer from "./reducer";

// 2. 按照状态函数建立store树(包括state 和 reducer)
const store = configureStore({
    reducer: reducer, // 右边是所写状态函数名,左边是指按列表存store树对象,名字任取。
})

export default store;

3.3 store 树相应内容(包括state、reducer)绑定对应组件

Calculator 组件:

// calculator.jsx
// 绑定 state :用于 output 的两个区域显示
const mapStateToProps = (state,props) => {
    return {
        current: state.currentOperand,
        last: state.lastOperand,
        operator: state.operator,
    }  
}

// 绑定 reducer(action,value) : AC 键的清空 ,Del 的回退,= 的执行
const mapDispachToProps = {
    delete: ()=>{
        return {
            type: ACTIONS.DELETE_DIGIT,
        }
    },
    clear: ()=>{
        return {
            type: ACTIONS.CLEAR,
        }
    },
    evaluate: ()=>{
        return {
            type: ACTIONS.EVALUTE,
        }
    }
}
// connect 绑定
export default connect(mapStateToProps,mapDispachToProps)(Calculator);

DigitButton 组件:

// digitButton .jsx
// 绑定 reducer(action,value):currentOperand 的 add_digit
const mapDispachToProps = {
    add_digit: (digit) => {
        return {
            type: ACTIONS.ADD_DIGIT,
            digit: digit,
        }
    }
};
// connect 绑定
export default connect(null, mapDispachToProps)(DigitButton);

OperatorButton 组件:

// operatorButton .jsx
// 绑定 reducer(action,value):currentOperand 的 add_digit
const mapDispachToProps = {
    choose_operator: (operator) => {
        return {
            type: ACTIONS.CHOOSE_OPERATOR,
            operator: operator,
        }
    }
};
export default connect(null,mapDispachToProps)(OperatorButton);

3.4 Button 单击事件绑定相应 mapDispachToProps 中的函数
(组件建立中的代码,按钮的单击已绑定)

三、最终呈现效果

在这里插入图片描述

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 18:43:07  更:2022-07-20 18:45:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 2:27:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码