Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage
Expand Down
8 changes: 8 additions & 0 deletions REQUIREMENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 요구 사항

### Step 1

- [x] Class Component를 사용합니다.
- [x] 레벨1을 참고하여 REQUIREMENTS.md에 요구 사항 도출
- [x] 2개의 숫자에 대해 덧셈이 가능하다.
Expand All @@ -12,3 +14,9 @@
- [x] 출력값 있는 상황에 사용자의 페이지 이탈시 confirm을 활용해 사용자의 이탈 여부를 확인한다.
- [x] 항상 사용자의 이탈시 마지막 출력값을 Local Storage에 저장한다.
- [x] 연산의 결과값이 Infinity일 경우 오류라는 문자열을 보여준다. (아이폰 참고)

### Step 2

- [x] Step1의 Class Component를 Function Component로 마이그레이션 합니다.
- [x] Step1에서의 피드백을 적용합니다.
- [x] 전체 코드를 리팩토링 합니다.
31 changes: 4 additions & 27 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -1,43 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<html lang="ko">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
content="react-calculator created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>리액트 계산기</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<noscript>자바스크립트를 "사용"으로 설정해주세요.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
1 change: 0 additions & 1 deletion public/robots.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
137 changes: 0 additions & 137 deletions src/App.js

This file was deleted.

97 changes: 97 additions & 0 deletions src/Calculator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React, { useEffect, useState, useCallback } from "react";
import DisplayResult from "./component/DisplayResult";
import NumberButton from "./component/buttons/NumberButton";
import ModifierButton from "./component/buttons/ModifierButton";
import OperatorButton from "./component/buttons/OperatorButton";
import calculate from "./utils/calculate";

import "./css/calculator.css";

export default function App() {
const [firstNumber, setFirstNumber] = useState(0);
const [operator, setOperator] = useState(null);
const [secondNumber, setSecondNumber] = useState(0);
const [isFirstNumber, setIsFirstNumber] = useState(true);
const [result, setResult] = useState(0);

useEffect(() => {
const prevValue = localStorage.getItem("prevValue") || result;
setFirstNumber(Number(prevValue));
setResult(Number(prevValue));

window.addEventListener("beforeunload", function (e) {
e.preventDefault();
e.returnValue = "";
});
}, []);

const onClickNumber = (e) => {
const inputNumber = e.target.textContent;
const resultNumber = result === 0 ? inputNumber : result + inputNumber;
setResult(resultNumber);

if (isFirstNumber) {
setFirstNumber(firstNumber * 10 + Number(inputNumber));
return;
}

setSecondNumber(secondNumber * 10 + Number(inputNumber));
};

const onClickOperator = (e) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

보통 실제 onClick 이벤트에 로직을 실행하는 함수들을 handle~~ 이렇게 적어놓는편이에요

if (firstNumber === "") return;

const inputOperator = e.target.textContent;
if (inputOperator === "=" && secondNumber === "") return;

if (inputOperator !== "=") {
setResult(result + inputOperator);
setOperator(inputOperator);
setIsFirstNumber(false);
return;
}

onCalculate();
};

const onClickModifier = useCallback(() => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useCallback을 사용하신 이유가 있나요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react에서는 컴포넌트가 리렌더 될때마다 함수를 새로 만들어 할당한다고 들었습니다!
이때 useCallback을 사용하면 리렌더시에 새로 만들지 않고, 기억해둔 함수를 할당해준다고 해서 적용해보았어요 :)
내용이 바뀔 염려가 없는 함수이니만큼 useCallback을 적용하기 좋다고 생각했습니다!

setFirstNumber(0);
setSecondNumber(0);
setOperator(null);
setIsFirstNumber(true);
setResult(0);
localStorage.setItem("prevValue", 0);
}, []);

const onCalculate = () => {
const res = calculate(firstNumber, secondNumber, operator);

if (res === Infinity || isNaN(res)) {
setFirstNumber(0);
setSecondNumber(0);
setResult("오류");
setOperator(null);
setIsFirstNumber(true);
Comment on lines +70 to +74

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다음미션부터는 setState가 많다면 한번쯤 묶어서 관리해도 되지 않나 생각해보면 좋을것 같아요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 상태 그대로 배열로 묶어 관리하거나, 상태 관리 로직이 늘어난다면 useReducer도 사용해보겠습니다!

localStorage.setItem("prevValue", "오류");
return;
}

setFirstNumber(res);
setSecondNumber(0);
setResult(res);
setOperator(null);
setIsFirstNumber(true);
localStorage.setItem("prevValue", res);
};

return (
<div id="app">
<div className="calculator">
<DisplayResult result={result} />
<NumberButton onClick={onClickNumber} />
<ModifierButton onClick={onClickModifier} />
<OperatorButton onClick={onClickOperator} />
</div>
</div>
);
}
69 changes: 0 additions & 69 deletions src/component/CalculatorButton.js

This file was deleted.

8 changes: 3 additions & 5 deletions src/component/DisplayResult.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import React, { Component } from "react";
import React from "react";

export default class DisplayResult extends Component {
render() {
return <h1 id="total">{this.props.result.toString()}</h1>;
}
export default function DisplayResult(props) {
return <h1 id="total">{props.result.toString()}</h1>;
}
Loading