React calculator
My company is going to make a big leap soon, and since we are currently still running on AngularJS 1.3.X we decided to make the switch to react for a myriad of reasons besides the obvious. Being the lead developer I decided to start messing around a bit.
After my first bit of research and hello world apps, I got to work on this calculator. It was made in about 30 minutes.
I was curious about my use of the state type properties, and if its the right way to tackle these kind of things. Any and all input is very much appreciated!
var Calculator = React.createClass({
getInitialState: function() {
return {
currentNumber: 0,
}
},
addDigit: function(n) {
if(this.state.currentNumber == 0){
this.setState({
currentNumber: n
})
} else {
this.setState({
currentNumber: "" + this.state.currentNumber + n
})
}
},
setOperator: function(f) {
if(this.state.currentNumber.slice(-1) != " "){
this.setState({
currentNumber: "" + this.state.currentNumber + " " + f + " "
})
}
},
solve: function() {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
},
clear: function() {
this.setState({
currentNumber: 0
})
},
render: function() {
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber}/>
<div>
<button onClick={this.clear} className="btn-1">C</button>
<button onClick={()=>this.setOperator('/')} className="btn-1">/</button>
<button onClick={()=>this.setOperator('*')} className="btn-1">*</button>
<button onClick={()=>this.setOperator('-')} className="btn-1">-</button>
</div>
<div>
<button onClick={()=>this.addDigit(7)} className="btn-1">7</button>
<button onClick={()=>this.addDigit(8)} className="btn-1">8</button>
<button onClick={()=>this.addDigit(9)} className="btn-1">9</button>
<button onClick={()=>this.setOperator('+')} className="btn-1">+</button>
</div>
<div>
<button onClick={()=>this.addDigit(4)} className="btn-1">4</button>
<button onClick={()=>this.addDigit(5)} className="btn-1">5</button>
<button onClick={()=>this.addDigit(6)} className="btn-1">6</button>
<button onClick={()=>this.setOperator('+')} className="btn-1">+</button>
</div>
<div>
<button onClick={()=>this.addDigit(1)} className="btn-1">1</button>
<button onClick={()=>this.addDigit(2)} className="btn-1">2</button>
<button onClick={()=>this.addDigit(3)} className="btn-1">3</button>
<button onClick={this.solve} className="btn-1">E</button>
</div>
<div>
<button onClick={()=>this.addDigit(0)} className="btn-1">0</button>
<button onClick={()=>this.addDigit(0)} className="btn-1">0</button>
<button onClick={()=>this.addDigit('.')} className="btn-1">.</button>
<button onClick={this.solve} className="btn-1">E</button>
</div>
</div>
</div>
)
}
});
ReactDOM.render(
<Calculator />,
document.getElementById('root1')
);
I used React, ReactDOM and Babel.
object-oriented calculator react.js jsx babel.js
add a comment |
My company is going to make a big leap soon, and since we are currently still running on AngularJS 1.3.X we decided to make the switch to react for a myriad of reasons besides the obvious. Being the lead developer I decided to start messing around a bit.
After my first bit of research and hello world apps, I got to work on this calculator. It was made in about 30 minutes.
I was curious about my use of the state type properties, and if its the right way to tackle these kind of things. Any and all input is very much appreciated!
var Calculator = React.createClass({
getInitialState: function() {
return {
currentNumber: 0,
}
},
addDigit: function(n) {
if(this.state.currentNumber == 0){
this.setState({
currentNumber: n
})
} else {
this.setState({
currentNumber: "" + this.state.currentNumber + n
})
}
},
setOperator: function(f) {
if(this.state.currentNumber.slice(-1) != " "){
this.setState({
currentNumber: "" + this.state.currentNumber + " " + f + " "
})
}
},
solve: function() {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
},
clear: function() {
this.setState({
currentNumber: 0
})
},
render: function() {
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber}/>
<div>
<button onClick={this.clear} className="btn-1">C</button>
<button onClick={()=>this.setOperator('/')} className="btn-1">/</button>
<button onClick={()=>this.setOperator('*')} className="btn-1">*</button>
<button onClick={()=>this.setOperator('-')} className="btn-1">-</button>
</div>
<div>
<button onClick={()=>this.addDigit(7)} className="btn-1">7</button>
<button onClick={()=>this.addDigit(8)} className="btn-1">8</button>
<button onClick={()=>this.addDigit(9)} className="btn-1">9</button>
<button onClick={()=>this.setOperator('+')} className="btn-1">+</button>
</div>
<div>
<button onClick={()=>this.addDigit(4)} className="btn-1">4</button>
<button onClick={()=>this.addDigit(5)} className="btn-1">5</button>
<button onClick={()=>this.addDigit(6)} className="btn-1">6</button>
<button onClick={()=>this.setOperator('+')} className="btn-1">+</button>
</div>
<div>
<button onClick={()=>this.addDigit(1)} className="btn-1">1</button>
<button onClick={()=>this.addDigit(2)} className="btn-1">2</button>
<button onClick={()=>this.addDigit(3)} className="btn-1">3</button>
<button onClick={this.solve} className="btn-1">E</button>
</div>
<div>
<button onClick={()=>this.addDigit(0)} className="btn-1">0</button>
<button onClick={()=>this.addDigit(0)} className="btn-1">0</button>
<button onClick={()=>this.addDigit('.')} className="btn-1">.</button>
<button onClick={this.solve} className="btn-1">E</button>
</div>
</div>
</div>
)
}
});
ReactDOM.render(
<Calculator />,
document.getElementById('root1')
);
I used React, ReactDOM and Babel.
object-oriented calculator react.js jsx babel.js
add a comment |
My company is going to make a big leap soon, and since we are currently still running on AngularJS 1.3.X we decided to make the switch to react for a myriad of reasons besides the obvious. Being the lead developer I decided to start messing around a bit.
After my first bit of research and hello world apps, I got to work on this calculator. It was made in about 30 minutes.
I was curious about my use of the state type properties, and if its the right way to tackle these kind of things. Any and all input is very much appreciated!
var Calculator = React.createClass({
getInitialState: function() {
return {
currentNumber: 0,
}
},
addDigit: function(n) {
if(this.state.currentNumber == 0){
this.setState({
currentNumber: n
})
} else {
this.setState({
currentNumber: "" + this.state.currentNumber + n
})
}
},
setOperator: function(f) {
if(this.state.currentNumber.slice(-1) != " "){
this.setState({
currentNumber: "" + this.state.currentNumber + " " + f + " "
})
}
},
solve: function() {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
},
clear: function() {
this.setState({
currentNumber: 0
})
},
render: function() {
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber}/>
<div>
<button onClick={this.clear} className="btn-1">C</button>
<button onClick={()=>this.setOperator('/')} className="btn-1">/</button>
<button onClick={()=>this.setOperator('*')} className="btn-1">*</button>
<button onClick={()=>this.setOperator('-')} className="btn-1">-</button>
</div>
<div>
<button onClick={()=>this.addDigit(7)} className="btn-1">7</button>
<button onClick={()=>this.addDigit(8)} className="btn-1">8</button>
<button onClick={()=>this.addDigit(9)} className="btn-1">9</button>
<button onClick={()=>this.setOperator('+')} className="btn-1">+</button>
</div>
<div>
<button onClick={()=>this.addDigit(4)} className="btn-1">4</button>
<button onClick={()=>this.addDigit(5)} className="btn-1">5</button>
<button onClick={()=>this.addDigit(6)} className="btn-1">6</button>
<button onClick={()=>this.setOperator('+')} className="btn-1">+</button>
</div>
<div>
<button onClick={()=>this.addDigit(1)} className="btn-1">1</button>
<button onClick={()=>this.addDigit(2)} className="btn-1">2</button>
<button onClick={()=>this.addDigit(3)} className="btn-1">3</button>
<button onClick={this.solve} className="btn-1">E</button>
</div>
<div>
<button onClick={()=>this.addDigit(0)} className="btn-1">0</button>
<button onClick={()=>this.addDigit(0)} className="btn-1">0</button>
<button onClick={()=>this.addDigit('.')} className="btn-1">.</button>
<button onClick={this.solve} className="btn-1">E</button>
</div>
</div>
</div>
)
}
});
ReactDOM.render(
<Calculator />,
document.getElementById('root1')
);
I used React, ReactDOM and Babel.
object-oriented calculator react.js jsx babel.js
My company is going to make a big leap soon, and since we are currently still running on AngularJS 1.3.X we decided to make the switch to react for a myriad of reasons besides the obvious. Being the lead developer I decided to start messing around a bit.
After my first bit of research and hello world apps, I got to work on this calculator. It was made in about 30 minutes.
I was curious about my use of the state type properties, and if its the right way to tackle these kind of things. Any and all input is very much appreciated!
var Calculator = React.createClass({
getInitialState: function() {
return {
currentNumber: 0,
}
},
addDigit: function(n) {
if(this.state.currentNumber == 0){
this.setState({
currentNumber: n
})
} else {
this.setState({
currentNumber: "" + this.state.currentNumber + n
})
}
},
setOperator: function(f) {
if(this.state.currentNumber.slice(-1) != " "){
this.setState({
currentNumber: "" + this.state.currentNumber + " " + f + " "
})
}
},
solve: function() {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
},
clear: function() {
this.setState({
currentNumber: 0
})
},
render: function() {
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber}/>
<div>
<button onClick={this.clear} className="btn-1">C</button>
<button onClick={()=>this.setOperator('/')} className="btn-1">/</button>
<button onClick={()=>this.setOperator('*')} className="btn-1">*</button>
<button onClick={()=>this.setOperator('-')} className="btn-1">-</button>
</div>
<div>
<button onClick={()=>this.addDigit(7)} className="btn-1">7</button>
<button onClick={()=>this.addDigit(8)} className="btn-1">8</button>
<button onClick={()=>this.addDigit(9)} className="btn-1">9</button>
<button onClick={()=>this.setOperator('+')} className="btn-1">+</button>
</div>
<div>
<button onClick={()=>this.addDigit(4)} className="btn-1">4</button>
<button onClick={()=>this.addDigit(5)} className="btn-1">5</button>
<button onClick={()=>this.addDigit(6)} className="btn-1">6</button>
<button onClick={()=>this.setOperator('+')} className="btn-1">+</button>
</div>
<div>
<button onClick={()=>this.addDigit(1)} className="btn-1">1</button>
<button onClick={()=>this.addDigit(2)} className="btn-1">2</button>
<button onClick={()=>this.addDigit(3)} className="btn-1">3</button>
<button onClick={this.solve} className="btn-1">E</button>
</div>
<div>
<button onClick={()=>this.addDigit(0)} className="btn-1">0</button>
<button onClick={()=>this.addDigit(0)} className="btn-1">0</button>
<button onClick={()=>this.addDigit('.')} className="btn-1">.</button>
<button onClick={this.solve} className="btn-1">E</button>
</div>
</div>
</div>
)
}
});
ReactDOM.render(
<Calculator />,
document.getElementById('root1')
);
I used React, ReactDOM and Babel.
object-oriented calculator react.js jsx babel.js
object-oriented calculator react.js jsx babel.js
edited Aug 16 '17 at 17:48
200_success
129k15152414
129k15152414
asked Aug 16 '17 at 14:43
StaggStagg
285
285
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
Fully functional example :
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
solve = () => {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
}
clear = () => {
this.setState({
currentNumber: ''
})
}
render() {
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0'},
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber || '0'} />
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'/>
For this review I will be using a class extending React.Component
.
First, your initial data. By default your calculator will be showing a 0
in the input field. Since this value is simply going to be deleted once the user types anything into the calculator interface, I suggest setting the default value of your input into an empty string, and just showing a 0
on the render side when the string is empty :
State:
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
Clear function:
clear = () => {
this.setState({
currentNumber: ''
})
}
Render:
<input value={this.state.currentNumber || '0'} />
Next up, handling operators and values. Both operators and value will do the same thing, adding characters to your currentNumber
, the only difference being that operators will add spaces and cannot be displayed next to each other or at the beginning of the input.
When setting a state value depending on an older one, you should avoid using the raw this.state
and use the callback version of setState
, allowing you to get the previous state and avoid any unexpected behavior :
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
Finally, layout.
Every single button is made out of the same node, with the same classname. This part of the code can be drastically reduced. First, let's make an array containing what differs between every button: what is shown, and the action it does when you click it:
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0' },
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
By default, every button will call the addChar
function we made earlier, allowing use to reduce the code even further and make an nested array of JSON objects containing mostly button names.
We can now render it by mapping every element to make buttons:
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
Yes, I know this question is from 2017
I've worked with React after I posted this question for more then a year, I have to say it's nice to see my old code and how I've improved. Still, this gave some more ideas to optimise my logic in the future, Thanks for taking the time to answer this question :)
– Stagg
18 hours ago
You're welcome, glad it helped a year after posting it :)
– Treycos
17 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f173160%2freact-calculator%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Fully functional example :
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
solve = () => {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
}
clear = () => {
this.setState({
currentNumber: ''
})
}
render() {
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0'},
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber || '0'} />
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'/>
For this review I will be using a class extending React.Component
.
First, your initial data. By default your calculator will be showing a 0
in the input field. Since this value is simply going to be deleted once the user types anything into the calculator interface, I suggest setting the default value of your input into an empty string, and just showing a 0
on the render side when the string is empty :
State:
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
Clear function:
clear = () => {
this.setState({
currentNumber: ''
})
}
Render:
<input value={this.state.currentNumber || '0'} />
Next up, handling operators and values. Both operators and value will do the same thing, adding characters to your currentNumber
, the only difference being that operators will add spaces and cannot be displayed next to each other or at the beginning of the input.
When setting a state value depending on an older one, you should avoid using the raw this.state
and use the callback version of setState
, allowing you to get the previous state and avoid any unexpected behavior :
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
Finally, layout.
Every single button is made out of the same node, with the same classname. This part of the code can be drastically reduced. First, let's make an array containing what differs between every button: what is shown, and the action it does when you click it:
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0' },
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
By default, every button will call the addChar
function we made earlier, allowing use to reduce the code even further and make an nested array of JSON objects containing mostly button names.
We can now render it by mapping every element to make buttons:
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
Yes, I know this question is from 2017
I've worked with React after I posted this question for more then a year, I have to say it's nice to see my old code and how I've improved. Still, this gave some more ideas to optimise my logic in the future, Thanks for taking the time to answer this question :)
– Stagg
18 hours ago
You're welcome, glad it helped a year after posting it :)
– Treycos
17 hours ago
add a comment |
Fully functional example :
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
solve = () => {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
}
clear = () => {
this.setState({
currentNumber: ''
})
}
render() {
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0'},
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber || '0'} />
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'/>
For this review I will be using a class extending React.Component
.
First, your initial data. By default your calculator will be showing a 0
in the input field. Since this value is simply going to be deleted once the user types anything into the calculator interface, I suggest setting the default value of your input into an empty string, and just showing a 0
on the render side when the string is empty :
State:
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
Clear function:
clear = () => {
this.setState({
currentNumber: ''
})
}
Render:
<input value={this.state.currentNumber || '0'} />
Next up, handling operators and values. Both operators and value will do the same thing, adding characters to your currentNumber
, the only difference being that operators will add spaces and cannot be displayed next to each other or at the beginning of the input.
When setting a state value depending on an older one, you should avoid using the raw this.state
and use the callback version of setState
, allowing you to get the previous state and avoid any unexpected behavior :
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
Finally, layout.
Every single button is made out of the same node, with the same classname. This part of the code can be drastically reduced. First, let's make an array containing what differs between every button: what is shown, and the action it does when you click it:
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0' },
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
By default, every button will call the addChar
function we made earlier, allowing use to reduce the code even further and make an nested array of JSON objects containing mostly button names.
We can now render it by mapping every element to make buttons:
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
Yes, I know this question is from 2017
I've worked with React after I posted this question for more then a year, I have to say it's nice to see my old code and how I've improved. Still, this gave some more ideas to optimise my logic in the future, Thanks for taking the time to answer this question :)
– Stagg
18 hours ago
You're welcome, glad it helped a year after posting it :)
– Treycos
17 hours ago
add a comment |
Fully functional example :
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
solve = () => {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
}
clear = () => {
this.setState({
currentNumber: ''
})
}
render() {
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0'},
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber || '0'} />
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'/>
For this review I will be using a class extending React.Component
.
First, your initial data. By default your calculator will be showing a 0
in the input field. Since this value is simply going to be deleted once the user types anything into the calculator interface, I suggest setting the default value of your input into an empty string, and just showing a 0
on the render side when the string is empty :
State:
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
Clear function:
clear = () => {
this.setState({
currentNumber: ''
})
}
Render:
<input value={this.state.currentNumber || '0'} />
Next up, handling operators and values. Both operators and value will do the same thing, adding characters to your currentNumber
, the only difference being that operators will add spaces and cannot be displayed next to each other or at the beginning of the input.
When setting a state value depending on an older one, you should avoid using the raw this.state
and use the callback version of setState
, allowing you to get the previous state and avoid any unexpected behavior :
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
Finally, layout.
Every single button is made out of the same node, with the same classname. This part of the code can be drastically reduced. First, let's make an array containing what differs between every button: what is shown, and the action it does when you click it:
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0' },
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
By default, every button will call the addChar
function we made earlier, allowing use to reduce the code even further and make an nested array of JSON objects containing mostly button names.
We can now render it by mapping every element to make buttons:
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
Yes, I know this question is from 2017
Fully functional example :
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
solve = () => {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
}
clear = () => {
this.setState({
currentNumber: ''
})
}
render() {
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0'},
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber || '0'} />
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'/>
For this review I will be using a class extending React.Component
.
First, your initial data. By default your calculator will be showing a 0
in the input field. Since this value is simply going to be deleted once the user types anything into the calculator interface, I suggest setting the default value of your input into an empty string, and just showing a 0
on the render side when the string is empty :
State:
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
Clear function:
clear = () => {
this.setState({
currentNumber: ''
})
}
Render:
<input value={this.state.currentNumber || '0'} />
Next up, handling operators and values. Both operators and value will do the same thing, adding characters to your currentNumber
, the only difference being that operators will add spaces and cannot be displayed next to each other or at the beginning of the input.
When setting a state value depending on an older one, you should avoid using the raw this.state
and use the callback version of setState
, allowing you to get the previous state and avoid any unexpected behavior :
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
Finally, layout.
Every single button is made out of the same node, with the same classname. This part of the code can be drastically reduced. First, let's make an array containing what differs between every button: what is shown, and the action it does when you click it:
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0' },
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
By default, every button will call the addChar
function we made earlier, allowing use to reduce the code even further and make an nested array of JSON objects containing mostly button names.
We can now render it by mapping every element to make buttons:
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
Yes, I know this question is from 2017
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
solve = () => {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
}
clear = () => {
this.setState({
currentNumber: ''
})
}
render() {
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0'},
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber || '0'} />
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'/>
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
currentNumber: ''
}
}
addChar = char => ev => {
if (!(this.state.currentNumber || ' ').endsWith(' ') || !char.endsWith(' ')){
this.setState(prevState => ({
currentNumber: prevState.currentNumber + char
}))
}
}
solve = () => {
this.setState({
currentNumber: eval(this.state.currentNumber)
})
}
clear = () => {
this.setState({
currentNumber: ''
})
}
render() {
const layout = [
[
{ name: 'C', func: this.clear },
{ name: ' / ' },
{ name: ' * ' },
{ name: ' - ' },
],
[
{ name: '7' },
{ name: '8' },
{ name: '9' },
{ name: ' + ' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
{ name: ' + ' },
],
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: 'E', func: this.solve },
],
[
{ name: '0' },
{ name: '0'},
{ name: '.' },
{ name: 'E', func: this.solve },
]
]
return (
<div>
<h1>This here Cal-Q-lator</h1>
<div>
<input value={this.state.currentNumber || '0'} />
{layout.map((line, i) =>
<div key={i}>
{line.map((field, i2) => <button key={i2} onClick={field.func || this.addChar(field.name)} className="btn-1">{field.name.trim()}</button>)}
</div>
)}
</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'/>
edited yesterday
Toby Speight
23.5k639112
23.5k639112
answered 2 days ago
TreycosTreycos
23518
23518
I've worked with React after I posted this question for more then a year, I have to say it's nice to see my old code and how I've improved. Still, this gave some more ideas to optimise my logic in the future, Thanks for taking the time to answer this question :)
– Stagg
18 hours ago
You're welcome, glad it helped a year after posting it :)
– Treycos
17 hours ago
add a comment |
I've worked with React after I posted this question for more then a year, I have to say it's nice to see my old code and how I've improved. Still, this gave some more ideas to optimise my logic in the future, Thanks for taking the time to answer this question :)
– Stagg
18 hours ago
You're welcome, glad it helped a year after posting it :)
– Treycos
17 hours ago
I've worked with React after I posted this question for more then a year, I have to say it's nice to see my old code and how I've improved. Still, this gave some more ideas to optimise my logic in the future, Thanks for taking the time to answer this question :)
– Stagg
18 hours ago
I've worked with React after I posted this question for more then a year, I have to say it's nice to see my old code and how I've improved. Still, this gave some more ideas to optimise my logic in the future, Thanks for taking the time to answer this question :)
– Stagg
18 hours ago
You're welcome, glad it helped a year after posting it :)
– Treycos
17 hours ago
You're welcome, glad it helped a year after posting it :)
– Treycos
17 hours ago
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f173160%2freact-calculator%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown