extjs 在textfield后面加一个button_编写一个计算器 利用react hook + context

通过编写一个计算器 来加深 对 react hook 和 context api 的理解

a9c1294547c36b883cb24f84f7740732.png

我们要实现上面这样的计算器

第一步

利用 create-react-app 快速创建项目

npx create-react-app calculatorcd

随后你会得到一个这样的目录

475a5ffda7101cac644961aac040a418.png

第二步 构建程序主体结构

我们创建一个Calculator.js 的文件这个 文件里面需要 有计算器的展示显示屏,还有数字操作面板

import React from 'react';
import NumberButton from './NumberButton';
import FunctionButton from './FunctionButton';
import ClearButton from './ClearButton';
import Display from './Display';
import EqualButton from './EqualButton';
import BackButton from './BackButton';
import NegativeButton from './NegativeButton';
import { CalculatorStyles } from './styles/Styles';

const Calculator = () => (
  
  <CalculatorStyles>
    //计算器展示样式
    <div className="display">
      <h1>计算器</h1>
      //展示区
      <Display />
    </div>
     //操作区
    <div className="number-pad">
      <ClearButton />   //清空按钮
      <BackButton />    // 回退按钮
      <NegativeButton /> //正负号 按钮
      <FunctionButton buttonValue="/" />  //功能按钮 除法
      <NumberButton buttonValue={7} />    //数字按钮7 后面类似
      <NumberButton buttonValue={8} />
      <NumberButton buttonValue={9} />
      <FunctionButton buttonValue="*" />
      <NumberButton buttonValue={4} />
      <NumberButton buttonValue={5} />
      <NumberButton buttonValue={6} />
      <FunctionButton buttonValue="-" />
      <NumberButton buttonValue={1} />
      <NumberButton buttonValue={2} />
      <NumberButton buttonValue={3} />
      <FunctionButton buttonValue="+" />
      <div className="zero-button">
        <NumberButton buttonValue={0} />
      </div>
      <NumberButton buttonValue="." />
      <EqualButton /> //等于按钮
    </div>
  </CalculatorStyles>
);

好啦程序主体框架已经完成 下面我们分别来实现这些组件

首先是 CalculatorStyles

也就是计算器的样式表,这里使用了这个插件需要额外安装上

yarn add styled-components -s

//style.js code

import styled from 'styled-components';

export const CalculatorStyles = styled.div`
  background-color: #4abdac;
  width: 100%;
  min-height: 100vh;
  display: grid;
  justify-items: center;
  grid-template-rows: minmax(200px 350px) 1fr;
  grid-template-columns: 1fr;
  @media (max-width: 500px) {
    max-width: 90%;
    padding: 5%;
  }
  .display {
    font-family: 'Orbitron', serif;
    /* grid-area: display; */
    margin: 0 !important;
    width: 100%;
    @media (max-width: 500px) {
      width: 100%;
      max-height: 200px;
    }
    h1 {
      font-size: 4rem;
      color: white;
      text-align: center;
      @media (max-width: 500px) {
        font-size: 2rem;
      }
    }
  }
  .number-pad {
    /* grid-area: numbers; */
    display: grid;
    grid-gap: 10px;
    grid-template-columns: repeat(4, 1fr);
    padding: 0px 0px 30px;
    width: 450px;
    margin: 0 auto;
    @media (max-width: 500px) {
      width: 100%;
      margin: 0;
    }
    button {
      width: 100%;
      height: 80px;
      border-radius: 20px;
      border: 3px solid white;
      font-size: 2rem;
      color: white;
      font-family: 'Orbitron', serif;
      background: #e17055;
      &:focus {
        outline: none;
      }
      &:hover {
        border: 3px solid #dfe6e9;
        font-weight: 500;
      }
    }
    button.function-button {
      background-color: #2d3436;
    }
    button.white-button {
      color: #2d3436;
      background-color: white;
    }
  }
  .zero-button {
    grid-column: 1/3;
  }
`;

export const DisplayStyles = styled.div`
  display: grid;
  grid-template-rows: 90px 50px;
  grid-template-columns: 1fr;
  border: 4px solid white;
  max-width: 700px;
  margin: 10px auto;
  align-items: center;
  border-radius: 20px;
  background: #e17055;
  @media (max-width: 500px) {
    width: 95%;
    grid-template-rows: 60px 40px;
  }
  h2,
  p {
    text-align: center;
    color: white;
  }
  h2 {
    font-size: 2.5rem;
    margin: 0;
    text-align: right;
    border-bottom: 4px solid white;
    padding: 15px 20px;
    @media (max-width: 500px) {
      font-size: 1.5rem;
      padding: 10px;
    }
  }
  h2.long-main-display {
    font-size: 1.2rem;
  }
  p {
    margin: 5px 0;
    font-size: 1.3rem;
    @media (max-width: 500px) {
      font-size: 0.8rem;
    }
  }
  p.long-stored-display {
    font-size: 0.5rem;
  }
`;

接下来是实现计算器的展示区域

606d7c6cbfa25f613679d1430783e6ea.png
import React, { useContext } from 'react';
import { NumberContext } from './NumberProvider';
import { DisplayStyles } from './styles/Styles';

const Display = () => {
  //这里是调用 我们Context Provider 中提供的 方法和数据 在这里进行消费
  const { number, storedNumber, functionType } = useContext(NumberContext);
  return (
    <DisplayStyles>
      //计算结果的展示
      <h2 className={storedNumber && storedNumber.length > 12 ? 'long-main-display' : undefined}>
        {!number.length && !storedNumber ? '0' : number || storedNumber}
      </h2>
      //计算式的展示
      <p className={storedNumber && storedNumber.length > 12 ? 'long-stored-display' : undefined}>
        {!storedNumber ? '输入一些数字进行计算' : `${storedNumber} ${functionType} ${number}`}
      </p>
    </DisplayStyles>
  );
};

export default Display;

接下来是实现操作区域

75fb37eac9b34ede67be8479016a6bca.png
<div className="number-pad">
      <ClearButton />   //清空按钮
      <BackButton />    // 回退按钮
      <NegativeButton /> //正负号 按钮
      <FunctionButton buttonValue="/" />  //功能按钮 除法
      <NumberButton buttonValue={7} />    //数字按钮7 后面类似
      <NumberButton buttonValue={8} />
      <NumberButton buttonValue={9} />
      <FunctionButton buttonValue="*" />
      <NumberButton buttonValue={4} />
      <NumberButton buttonValue={5} />
      <NumberButton buttonValue={6} />
      <FunctionButton buttonValue="-" />
      <NumberButton buttonValue={1} />
      <NumberButton buttonValue={2} />
      <NumberButton buttonValue={3} />
      <FunctionButton buttonValue="+" />
      <div className="zero-button">
        <NumberButton buttonValue={0} />
      </div>
      <NumberButton buttonValue="." />
      <EqualButton /> //等于按钮
    </div>

可以看到操作区域主要分为功能按钮 <FunctionButton buttonValue="/" /> 
和数字按钮 <NumberButton buttonValue={7} />,
还有特殊功能按钮 比如<ClearButton />

对于特殊功能按钮 我们只讲一个因为其他的都类似只是调用的方法不同而已

下面是ClearButton 的实现

import React, { useContext } from 'react';
import { NumberContext } from './NumberProvider';

const ClearButton = () => {
  //这里利用Context调用了 清空方法
  const { handleClearValue } = useContext(NumberContext);
  return (
    <button type="button" className="white-button" onClick={() => handleClearValue()}>
      C
    </button>
  );
};

export default ClearButton;

FunctionButton 的实现、

import React, { useContext } from 'react';
import { NumberContext } from './NumberProvider';

const FunctionButton = ({ buttonValue }) => {
  //这里利用Context调用了 设置数字计算方式的方法并将参数传入
  const { handleSetCalcFunction } = useContext(NumberContext);
  return (
    <button className="function-button" type="button" onClick={() => handleSetCalcFunction(buttonValue)}>
      {buttonValue}
    </button>
  );
};

export default FunctionButton;

NumberButton 的实现

import React, { useContext } from 'react';
import { NumberContext } from './NumberProvider';

const CalculatorButton = ({ buttonValue }) => {
 //这里是设置数字
  const { handleSetDisplayValue } = useContext(NumberContext);
  return (
    <button type="button" onClick={() => handleSetDisplayValue(buttonValue)}>
      {buttonValue}
    </button>
  );
};

export default CalculatorButton;

主体结构已经完成,但是上面那些context 提供的方法还没有实现,由于context的特性它可以使我们可以轻松的在父组件中提供的值很方便的传递给下面的子组件,如果没有这个那么我们只能利用REDUX代替 context 或者 props 一级一级的传递.

Context NumberProvider的实现

import React, { useState } from 'react';

//创建Context
export const NumberContext = React.createContext();

const NumberProvider = props => {
  const [number, setNumber] = useState('');//数字
  const [storedNumber, setStoredNumber] = useState('');//存储数字
  const [functionType, setFunctionType] = useState('');//类型

  方法 具体实现
  const handleSetDisplayValue = num => {
    if ((!number.includes('.') || num !== '.') && number.length < 8) {
      setNumber(`${(number + num).replace(/^0+/, '')}`);
    }
  };
  方法 具体实现
  const handleSetStoredValue = () => {
    setStoredNumber(number);
    setNumber('');
  };

  //清空操作
  const handleClearValue = () => {
    setNumber('');
    setStoredNumber('');
    setFunctionType('');
  };
   方法 具体实现
  const handleBackButton = () => {
    if (number !== '') {
      const deletedNumber = number.slice(0, number.length - 1);
      setNumber(deletedNumber);
    }
  };
   方法 具体实现
  const handleSetCalcFunction = type => {
    if (number) {
      setFunctionType(type);
      handleSetStoredValue();
    }
    if (storedNumber) {
      setFunctionType(type);
    }
  };
 方法 具体实现
  const handleToggleNegative = () => {
    if (number) {
      if (number > 0) {
        setNumber(`-${number}`);
      } else {
        const positiveNumber = number.slice(1);
        setNumber(positiveNumber);
      }
    } else if (storedNumber > 0) {
      setStoredNumber(`-${storedNumber}`);
    } else {
      const positiveNumber = storedNumber.slice(1);
      setStoredNumber(positiveNumber);
    }
  };
  方法 具体实现
  const doMath = () => {
    if (number && storedNumber) {
      switch (functionType) {
        case '+':
          setStoredNumber(`${Math.round(`${(parseFloat(storedNumber) + parseFloat(number)) * 100}`) / 100}`);
          break;
        case '-':
          setStoredNumber(`${Math.round(`${(parseFloat(storedNumber) - parseFloat(number)) * 1000}`) / 1000}`);
          break;
        case '/':
          setStoredNumber(`${Math.round(`${(parseFloat(storedNumber) / parseFloat(number)) * 1000}`) / 1000}`);
          break;
        case '*':
          setStoredNumber(`${Math.round(`${parseFloat(storedNumber) * parseFloat(number) * 1000}`) / 1000}`);
          break;
        default:
          break;
      }
      setNumber('');
    }
  };

  return (
   //子组件就是通过这个来和父组件进行通信,我们可以看到这里提供的方法和数字 和 子组件中 的useContext Hook 可以对应上
    <NumberContext.Provider
      value={{
        doMath,
        functionType,
        handleBackButton,
        handleClearValue,
        handleSetCalcFunction,
        handleSetDisplayValue,
        handleSetStoredValue,
        handleToggleNegative,
        number,
        storedNumber,
        setNumber,
      }}
    >
      {props.children}
    </NumberContext.Provider>
  );
};

export default NumberProvider;

好了 程序完成 执行 yarn start 看看效果把

源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值