在本教程中,我们将构建一个React计算器应用程序。你将学习如何制作一个线框,设计一个布局,创建组件,更新状态,以及格式化输出。
为了激发你的灵感,这里有一个链接,可以看到我们要构建的部署项目。
另外,这里有源代码,如果你在项目的任何阶段需要帮助,可以参考一下。
计划
由于我们将建立一个计算器应用程序,让我们选择一个范围,不要太复杂的学习,但也不要太基本,以涵盖创建一个应用程序的不同方面。
我们要实现的功能包括。
- 加法、减法、乘法、除法
- 支持小数点值
- 计算百分比
- 反转数值
- 重置功能
- 格式化较大的数字
- 根据长度调整输出大小
首先,我们要画一个基本的线框来显示我们的想法。为此,你可以使用Figma或Diagrams.net这样的免费工具。
请注意,在这个阶段,考虑颜色和造型并不那么重要。最重要的是,你可以构建布局并确定所涉及的组件。
设计颜色
一旦我们处理好了布局和组件,完成设计所要做的就是选择一个漂亮的颜色方案。
下面是一些使应用程序看起来很棒的准则。
- 封装器应与背景形成对比
- 屏幕和按钮的数值应易于阅读
- 等号按钮应采用不同的颜色,以突出其重要性。
基于上述标准,我们将使用如下所示的颜色方案。
设置项目
要开始,在你的项目文件夹中打开终端,使用create-react-app创建一个模板。要做到这一点,运行该命令。
npx create-react-app calculator
这是最快和最简单的方法来设置一个完全工作的React应用,而且是零配置。之后你需要做的就是运行cd calculator
,切换到新创建的项目文件夹,然后npm start
,在浏览器中启动你的应用程序。
正如你所看到的,它带有一些默认的模板,所以接下来我们要在项目文件夹树中做一些清理工作。
找到src
文件夹,你的应用程序的逻辑将在这里,并删除除App.js
创建你的应用程序,index.css
样式你的应用程序,以及index.js
在DOM中渲染你的应用程序之外的所有东西。
创建组件
由于我们已经做了一些线框设计,我们已经知道了应用程序的主要构建模块。这些是Wrapper
、Screen
、ButtonBox
和Button
。
首先在src
文件夹内创建一个components
文件夹。然后我们将为每个组件创建一个单独的.js
文件和.css
文件。
如果你不想手动创建这些文件夹和文件,你可以使用下面的单行代码来快速完成设置。
cd src && mkdir components && cd components && touch Wrapper.js Wrapper.css Screen.js Screen.css ButtonBox.js ButtonBox.css Button.js Button.css
包裹者
Wrapper
组件将是一个框架,将所有的子组件固定在一起。它也将允许我们在之后将整个应用程序居中。
Wrapper.js
import "./Wrapper.css";
const Wrapper = ({ children }) => {
return <div className="wrapper">{children}</div>;
};
export default Wrapper;
Wrapper.css
.wrapper {
width: 340px;
height: 540px;
padding: 10px;
border-radius: 10px;
background-color: #485461;
background-image: linear-gradient(315deg, #485461 0%, #28313b 74%);
}
屏幕
Screen
组件将是Wrapper
组件的顶部部分子组件,其目的是显示计算值。
在功能列表中,我们包括显示输出在长度上的大小调整,这意味着较长的值必须缩小尺寸。我们将使用一个小的(3.4kb的gzip)库,叫做react-textfit,用于此。
要安装它,运行npm i react-textfit
,然后导入并使用它,如下图所示。
Screen.js
import { Textfit } from "react-textfit";
import "./Screen.css";
const Screen = ({ value }) => {
return (
<Textfit className="screen" mode="single" max={70}>
{value}
</Textfit>
);
};
export default Screen;
Screen.css
.screen {
height: 100px;
width: 100%;
margin-bottom: 10px;
padding: 0 10px;
background-color: #4357692d;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: flex-end;
color: white;
font-weight: bold;
box-sizing: border-box;
}
按钮盒
ButtonBox
组件,与Wrapper
组件类似,将成为子程序的框架 - 只是这次是为Button
组件。
ButtonBox.js
import "./ButtonBox.css";
const ButtonBox = ({ children }) => {
return <div className="buttonBox">{children}</div>;
};
export default ButtonBox;
ButtonBox.css
.buttonBox {
width: 100%;
height: calc(100% - 110px);
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(5, 1fr);
grid-gap: 10px;
}
按钮
Button
组件将为应用程序提供互动性。每个组件都会有value
和onClick
道具。
在样式表中,我们也将包括equal
按钮的样式。以后我们将使用Button
props来访问这个类。
Button.js
import "./Button.css";
const Button = ({ className, value, onClick }) => {
return (
<button className={className} onClick={onClick}>
{value}
</button>
);
};
export default Button;
Button.css
button {
border: none;
background-color: rgb(80, 60, 209);
font-size: 24px;
color: rgb(255, 255, 255);
font-weight: bold;
cursor: pointer;
border-radius: 10px;
outline: none;
}
button:hover {
background-color: rgb(61, 43, 184);
}
.equals {
grid-column: 3 / 5;
background-color: rgb(243, 61, 29);
}
.equals:hover {
background-color: rgb(228, 39, 15);
}
渲染元素
React应用程序中渲染的基础文件是index.js
。在我们进一步讨论之前,确保你的index.js
看起来如下。
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./index.css";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
另外,让我们检查一下index.css
,确保我们重置了padding
和margin
的默认值,挑选一些很棒的字体(在本例中像Montserrat),并设置适当的规则以使应用程序在视口中居中。
@import url("https://fonts.googleapis.com/css2?family=Montserrat&display=swap");
* {
margin: 0;
padding: 0;
font-family: "Montserrat", sans-serif;
}
body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #fbb034;
background-image: linear-gradient(315deg, #fbb034 0%, #ffdd00 74%);
}
最后,让我们打开主文件App.js
,并导入我们之前创建的所有组件。
import Wrapper from "./components/Wrapper";
import Screen from "./components/Screen";
import ButtonBox from "./components/ButtonBox";
import Button from "./components/Button";
const App = () => {
return (
<Wrapper>
<Screen value="0" />
<ButtonBox>
<Button
className=""
value="0"
onClick={() => {
console.log("Button clicked!");
}}
/>
</ButtonBox>
</Wrapper>
);
};
export default App;
在上面的例子中,我们只渲染了一个Button
组件。
让我们为线框中的数据创建一个数组表示,这样我们就可以映射并渲染ButtonBox
中的所有按钮。
import Wrapper from "./components/Wrapper";
import Screen from "./components/Screen";
import ButtonBox from "./components/ButtonBox";
import Button from "./components/Button";
const btnValues = [
["C", "+-", "%", "/"],
[7, 8, 9, "X"],
[4, 5, 6, "-"],
[1, 2, 3, "+"],
[0, ".", "="],
];
const App = () => {
return (
<Wrapper>
<Screen value=0 />
<ButtonBox>
{
btnValues.flat().map((btn, i) => {
return (
<Button
key={i}
className={btn === "=" ? "equals" : ""}
value={btn}
onClick={() => {
console.log(`${btn} clicked!`);
}}
/>
);
})
}
</ButtonBox>
</Wrapper>
);
};
检查你的终端,确保你的React应用仍在运行。如果没有,运行npm start
,再次启动它。
打开你的浏览器。如果你跟着做,你当前的结果应该是这样的。
如果你愿意,你也可以打开浏览器的devtools,测试一下每个按钮按下的日志值。
定义状态
接下来,我们将使用ReactuseState
钩子声明状态变量。
具体来说,会有三个状态。num
,输入值;sign
,选择的符号:和res
,计算值。
为了使用useState
钩子,我们必须首先在App.js
中导入它。
import React, { useState } from "react";
在App
函数中,我们将使用一个对象来一次性设置所有状态。
import React, { useState } from "react";
// ...
const App = () => {
let [calc, setCalc] = useState({
sign: "",
num: 0,
res: 0,
});
return (
// ...
);
};