Tutoriel React : créer une application de calculatrice à partir de zéro

Dans ce didacticiel, nous allons créer une application React Calculator. Vous apprendrez à créer une structure filaire, à concevoir une mise en page, à créer des composants, à mettre à jour les états et à formater la sortie.
Pour vous inspirer, voici un lien vers le projet déployé nous allons construire.
Aussi, voici le code sourcejuste pour référence si vous avez besoin d’aide à n’importe quelle étape du projet.
Planification
Puisque nous allons créer une application de calculatrice, choisissons une portée qui n’est pas trop compliquée pour l’apprentissage, mais aussi pas trop basique pour couvrir différents aspects de la création d’une application.
Les fonctionnalités que nous allons implémenter incluent :
- additionner, soustraire, multiplier, diviser
- prendre en charge les valeurs décimales
- calculer des pourcentages
- inverser les valeurs
- fonctionnalité de réinitialisation
- formater des nombres plus grands
- redimensionner la sortie en fonction de la longueur
Pour commencer, nous allons dessiner un wireframe de base pour afficher nos idées. Pour cela, vous pouvez utiliser des outils gratuits comme Figma ou alors Diagrammes.net.
Notez que, dans cette phase, il n’est pas si important de penser aux couleurs et au style. Le plus important est que vous puissiez structurer la mise en page et identifier les composants impliqués.
Couleurs de conception
Une fois que nous avons traité la mise en page et les composants, tout ce qu’il reste à faire pour terminer la conception est de choisir une belle palette de couleurs.
Vous trouverez ci-dessous quelques directives pour rendre l’application attrayante :
- l’emballage doit contraster avec le fond
- les valeurs de l’écran et des boutons doivent être faciles à lire
- le bouton égal doit être d’une couleur différente, pour donner un peu d’accent
Sur la base des critères ci-dessus, nous utiliserons la palette de couleurs ci-dessous.
Configuration du projet
Pour commencer, ouvrez le terminal dans votre dossier de projets et créez un modèle passe-partout à l’aide du créer-réagir-app. Pour ce faire, exécutez la commande :
npx create-react-app calculator
C’est le moyen le plus rapide et le plus simple de configurer une application React entièrement fonctionnelle sans aucune configuration. Tout ce que vous devez faire après cela est exécuté cd calculator
pour passer au dossier de projet nouvellement créé et npm start
pour démarrer votre application dans le navigateur.
Comme vous pouvez le voir, il est livré avec un passe-partout par défaut, donc nous allons ensuite faire un peu de nettoyage dans l’arborescence des dossiers du projet.
Trouvez le src
dossier, où la logique de votre application vivra, et supprimez tout sauf App.js
pour créer votre application, index.css
pour styliser votre application, et index.js
pour rendre votre application dans le DOM.
Créer des composants
Comme nous avons déjà fait du wireframing, nous connaissons déjà les principaux éléments constitutifs de l’application. Ce sont Wrapper
, Screen
, ButtonBox
et Button
.
Créez d’abord un components
dossier à l’intérieur du src
dossier. Nous allons ensuite créer un fichier séparé .js
fichier et .css
fichier pour chaque composant.
Si vous ne souhaitez pas créer ces dossiers et fichiers manuellement, vous pouvez utiliser la ligne suivante pour configurer rapidement les éléments :
cd src && mkdir components && cd components && touch Wrapper.js Wrapper.css Screen.js Screen.css ButtonBox.js ButtonBox.css Button.js Button.css
Emballage
Le Wrapper
composant sera le cadre, tenant tous les composants enfants en place. Cela nous permettra également de centrer l’ensemble de l’application par la suite.
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%);
}
Filtrer
Le Screen
composant sera l’enfant de la section supérieure du Wrapper
composant, et son but sera d’afficher les valeurs calculées.
Dans la liste des fonctionnalités, nous avons inclus le redimensionnement de la sortie d’affichage sur la longueur, ce qui signifie que les valeurs plus longues doivent réduire leur taille. Nous allons utiliser une petite bibliothèque (gzip de 3,4 Ko) appelée réagir-textfit pour ça.
Pour l’installer, lancez npm i react-textfit
puis importez-le et utilisez-le comme indiqué ci-dessous.
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;
Écran.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
Le ButtonBox
composant, à l’instar du Wrapper
composant, sera le cadre pour les enfants – seulement cette fois pour le Button
Composants.
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;
}
Bouton
Le Button
fournira l’interactivité de l’application. Chaque composant aura le value
et onClick
accessoires.
Dans la feuille de style, nous inclurons également les styles pour le equal
bouton. Nous utiliserons Button
props pour accéder à la classe plus tard.
Bouton.js
import "./Button.css";
const Button = ({ className, value, onClick }) => {
return (
<button className={className} onClick={onClick}>
{value}
</button>
);
};
export default Button;
Bouton.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);
}
Éléments de rendu
Le fichier de base pour le rendu dans les applications React est index.js
. Avant d’aller plus loin, assurez-vous que votre index.js
se présente comme suit :
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")
);
Aussi, vérifions index.css
et assurez-vous que nous réinitialisons les valeurs par défaut pour padding
et margin
choisissez une bonne police (comme Montserrat dans ce cas) et définissez les règles appropriées pour centrer l’application dans la fenêtre :
@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%);
}
Enfin, ouvrons le fichier principal App.js
et importez tous les composants que nous avons créés précédemment :
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;
Dans l’exemple ci-dessus, nous n’avons rendu qu’un seul Button
composant.
Créons une représentation matricielle des données dans le wireframe, afin que nous puissions mapper et rendre tous les boutons dans le ButtonBox
:
import Wrapper from "./components/Wrapper";
import Screen from "./components/Screen";
import ButtonBox from "./components/ButtonBox";
import Button from "./components/Button";
const btnValues = [
["C", "+-", "%", "https://www.sitepoint.com/"],
[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>
);
};
Vérifiez votre terminal et assurez-vous que votre application React est toujours en cours d’exécution. Sinon, lancez-vous npm start
pour le recommencer.
Ouvrez votre navigateur. Si vous avez suivi, votre résultat actuel devrait ressembler à ceci :
Si vous le souhaitez, vous pouvez également ouvrir les outils de développement du navigateur et tester les valeurs du journal pour chaque bouton enfoncé.
Définir les états
Ensuite, nous allons déclarer les variables d’état en utilisant React useState
crochet.
Plus précisément, il y aura trois états : num
la valeur saisie ; sign
le signe sélectionné : et res
la valeur calculée.
Afin d’utiliser le useState
crochet, nous devons d’abord l’importer dans App.js
:
import React, { useState } from "react";
Dans le App
fonction, nous utiliserons un objet pour définir tous les états à la fois :
import React, { useState } from "react";
const App = () => {
let [calc, setCalc] = useState({
sign: "",
num: 0,
res: 0,
});
return (
);
};
Fonctionnalité
Notre application a l’air bien, mais il n’y a aucune fonctionnalité. Actuellement, il ne peut sortir que les valeurs des boutons dans la console du navigateur. Réparons ça !
Nous allons commencer par le Screen
composant. Définissez la logique conditionnelle suivante sur value
prop, il affiche donc le nombre saisi (si le nombre est saisi) ou le résultat calculé (si le bouton égal est enfoncé).
Pour cela, nous utiliserons le JS intégré opérateur ternairequi est essentiellement un raccourci pour le if
déclaration, en prenant une expression et en renvoyant une valeur après ?
si l’expression est vraie, ou après :
si l’expression est fausse :
<Screen value={calc.num ? calc.num : calc.res} />
Modifions maintenant le Button
composant afin qu’il puisse détecter différents types de boutons et exécuter la fonction attribuée une fois que le bouton spécifique est enfoncé. Utilisez le code ci-dessous :
import React, { useState } from "react";
const App = () => {
return (
<Wrapper>
<Screen value={calc.num ? calc.num : calc.res} />
<ButtonBox>
{btnValues.flat().map((btn, i) => {
return (
<Button
key={i}
className={btn === "=" ? "equals" : ""}
value={btn}
onClick={
btn === "C"
? resetClickHandler
: btn === "+-"
? invertClickHandler
: btn === "%"
? percentClickHandler
: btn === "="
? equalsClickHandler
: btn === "https://www.sitepoint.com/" || btn === "X" || btn === "-" || btn === "+"
? signClickHandler
: btn === "."
? commaClickHandler
: numClickHandler
}
/>
);
})}
</ButtonBox>
</Wrapper>
);
};
Nous sommes maintenant prêts à créer toutes les fonctions nécessaires.
numClickHandler
Le numClickHandler
La fonction se déclenche uniquement si l’une des touches numériques (0 à 9) est enfoncée. Il obtient alors la valeur de Button
et ajoute cela au courant num
valeur.
Il s’assurera également que :
- aucun nombre entier ne commence par zéro
- il n’y a pas de multiples zéros avant la virgule
- le format sera « 0 ». si « . » est pressé en premier
- les nombres sont entrés jusqu’à 16 nombres entiers
import React, { useState } from "react";
const App = () => {
const numClickHandler = (e) => {
e.preventDefault();
const value = e.target.innerHTML;
if (calc.num.length < 16) {
setCalc({
...calc,
num:
calc.num === 0 && value === "0"
? "0"
: calc.num % 1 === 0
? Number(calc.num + value)
: calc.num + value,
res: !calc.sign ? 0 : calc.res,
});
}
};
return (
);
};
virguleClicHandler
Le commaClickHandler
La fonction n’est déclenchée que si le point décimal (.
) est enfoncé. Il ajoute le point décimal au courant num
valeur, ce qui en fait un nombre décimal.
Il s’assurera également qu’aucun point décimal multiple n’est possible.
Remarque : J’ai appelé la fonction de gestion « commaClickHandler » car, dans de nombreuses régions du monde, les entiers et les décimales sont séparés par une virgule et non par un point décimal.
const commaClickHandler = (e) => {
e.preventDefault();
const value = e.target.innerHTML;
setCalc({
...calc,
num: !calc.num.toString().includes(".") ? calc.num + value : calc.num,
});
};
signClickHandler
Le signClickHandler
la fonction est déclenchée lorsque l’utilisateur appuie sur l’un ou l’autre +, –, * ou alors /. La valeur particulière est alors définie comme un courant sign
valeur dans le calc
objet.
Il s’assurera également qu’il n’y a aucun effet sur les appels répétés :
const signClickHandler = (e) => {
e.preventDefault();
const value = e.target.innerHTML;
setCalc({
...calc,
sign: value,
res: !calc.res && calc.num ? calc.num : calc.res,
num: 0,
});
};
equalsClickHandler
Le equalsClickHandler
calcule le résultat lorsque le bouton égal (=) est enfoncé. Le calcul est basé sur le courant num
et res
valeur, ainsi que la sign
sélectionné (voir math
une fonction).
La valeur renvoyée est alors définie comme la nouvelle res
pour les calculs ultérieurs.
Il s’assurera également que :
- il n’y a aucun effet sur les appels répétés
- les utilisateurs ne peuvent pas diviser par 0
const equalsClickHandler = () => {
if (calc.sign && calc.num) {
const math = (a, b, sign) =>
sign === "+"
? a + b
: sign === "-"
? a - b
: sign === "X"
? a * b
: a / b;
setCalc({
...calc,
res:
calc.num === "0" && calc.sign === "https://www.sitepoint.com/"
? "Can't divide with 0"
: math(Number(calc.res), Number(calc.num), calc.sign),
sign: "",
num: 0,
});
}
};
invertClickHandler
Le invertClickHandler
la fonction vérifie d’abord s’il y a une valeur entrée (num
) ou valeur calculée (res
) puis les inverse en multipliant par -1 :
const invertClickHandler = () => {
setCalc({
...calc,
num: calc.num ? calc.num * -1 : 0,
res: calc.res ? calc.res * -1 : 0,
sign: "",
});
};
pourcentageClicHandler
Le percentClickHandler
la fonction vérifie s’il y a une valeur entrée (num
) ou valeur calculée (res
), puis calcule le pourcentage à l’aide de la fonction intégrée Math.pow
fonction, qui renvoie la base à la puissance exposant :
const percentClickHandler = () => {
let num = calc.num ? parseFloat(calc.num) : 0;
let res = calc.res ? parseFloat(calc.res) : 0;
setCalc({
...calc,
num: (num /= Math.pow(100, 1)),
res: (res /= Math.pow(100, 1)),
sign: "",
});
};
resetClickHandler
Le resetClickHandler
la fonction utilise par défaut toutes les valeurs initiales de calc
renvoyant le calc
état tel qu’il était lors du premier rendu de l’application Calculatrice :
const resetClickHandler = () => {
setCalc({
...calc,
sign: "",
num: 0,
res: 0,
});
};
Formatage d’entrée
Une dernière chose pour compléter la liste des fonctionnalités dans l’intro serait d’implémenter le formatage des valeurs. Pour cela, nous pourrions utiliser une chaîne Regex modifiée publiée par Émissaire:
const toLocaleString = (num) =>
String(num).replace(/(?<!\..*)(\d)(?=(?:\d{3})+(?:\.|$))/g, "$1 ");
Essentiellement, ce qu’il fait est de prendre un nombre, de le formater au format chaîne et de créer les séparateurs d’espace pour le millier.
Si nous inversons le processus et voulons traiter la chaîne de nombres, nous devons d’abord supprimer les espaces, afin que nous puissions ensuite la convertir en nombre. Pour cela, vous pouvez utiliser cette fonction :
const removeSpaces = (num) => num.toString().replace(/\s/g, "");
Voici le code où vous devez inclure les deux fonctions :
import React, { useState } from "react";
const toLocaleString = (num) =>
String(num).replace(/(?<!\..*)(\d)(?=(?:\d{3})+(?:\.|$))/g, "$1 ");
const removeSpaces = (num) => num.toString().replace(/\s/g, "");
const App = () => {
return (
);
};
Consultez la section suivante avec le code complet sur la façon d’ajouter toLocaleString
et removeSpaces
aux fonctions de gestionnaire pour le Button
composant.
Mettre tous ensemble
Si vous avez suivi, l’ensemble App.js
le code devrait ressembler à ceci :
import React, { useState } from "react";
import Wrapper from "./components/Wrapper";
import Screen from "./components/Screen";
import ButtonBox from "./components/ButtonBox";
import Button from "./components/Button";
const btnValues = [
["C", "+-", "%", "https://www.sitepoint.com/"],
[7, 8, 9, "X"],
[4, 5, 6, "-"],
[1, 2, 3, "+"],
[0, ".", "="],
];
const toLocaleString = (num) =>
String(num).replace(/(?<!\..*)(\d)(?=(?:\d{3})+(?:\.|$))/g, "$1 ");
const removeSpaces = (num) => num.toString().replace(/\s/g, "");
const App = () => {
let [calc, setCalc] = useState({
sign: "",
num: 0,
res: 0,
});
const numClickHandler = (e) => {
e.preventDefault();
const value = e.target.innerHTML;
if (removeSpaces(calc.num).length < 16) {
setCalc({
...calc,
num:
calc.num === 0 && value === "0"
? "0"
: removeSpaces(calc.num) % 1 === 0
? toLocaleString(Number(removeSpaces(calc.num + value)))
: toLocaleString(calc.num + value),
res: !calc.sign ? 0 : calc.res,
});
}
};
const commaClickHandler = (e) => {
e.preventDefault();
const value = e.target.innerHTML;
setCalc({
...calc,
num: !calc.num.toString().includes(".") ? calc.num + value : calc.num,
});
};
const signClickHandler = (e) => {
e.preventDefault();
const value = e.target.innerHTML;
setCalc({
...calc,
sign: value,
res: !calc.res && calc.num ? calc.num : calc.res,
num: 0,
});
};
const equalsClickHandler = () => {
if (calc.sign && calc.num) {
const math = (a, b, sign) =>
sign === "+"
? a + b
: sign === "-"
? a - b
: sign === "X"
? a * b
: a / b;
setCalc({
...calc,
res:
calc.num === "0" && calc.sign === "https://www.sitepoint.com/"
? "Can't divide with 0"
: toLocaleString(
math(
Number(removeSpaces(calc.res)),
Number(removeSpaces(calc.num)),
calc.sign
)
),
sign: "",
num: 0,
});
}
};
const invertClickHandler = () => {
setCalc({
...calc,
num: calc.num ? toLocaleString(removeSpaces(calc.num) * -1) : 0,
res: calc.res ? toLocaleString(removeSpaces(calc.res) * -1) : 0,
sign: "",
});
};
const percentClickHandler = () => {
let num = calc.num ? parseFloat(removeSpaces(calc.num)) : 0;
let res = calc.res ? parseFloat(removeSpaces(calc.res)) : 0;
setCalc({
...calc,
num: (num /= Math.pow(100, 1)),
res: (res /= Math.pow(100, 1)),
sign: "",
});
};
const resetClickHandler = () => {
setCalc({
...calc,
sign: "",
num: 0,
res: 0,
});
};
return (
<Wrapper>
<Screen value={calc.num ? calc.num : calc.res} />
<ButtonBox>
{btnValues.flat().map((btn, i) => {
return (
<Button
key={i}
className={btn === "=" ? "equals" : ""}
value={btn}
onClick={
btn === "C"
? resetClickHandler
: btn === "+-"
? invertClickHandler
: btn === "%"
? percentClickHandler
: btn === "="
? equalsClickHandler
: btn === "https://www.sitepoint.com/" || btn === "X" || btn === "-" || btn === "+"
? signClickHandler
: btn === "."
? commaClickHandler
: numClickHandler
}
/>
);
})}
</ButtonBox>
</Wrapper>
);
};
export default App;
Remarques finales
Toutes nos félicitations! Vous avez créé une application entièrement fonctionnelle et stylisée. J’espère que vous avez appris une chose ou deux au cours du processus !
D’autres idées à explorer seraient d’ajouter des fonctionnalités scientifiques ou d’implémenter la mémoire avec la liste des calculs précédents.
Si vous avez des rapports de problèmes ou des demandes de fonctionnalités, n’hésitez pas à les laisser dans le Dépôt GitHub. Si vous aimez le projet, n’hésitez pas à le mettre en vedette.
Source link