Hello, FreeCodeCampers.
I am currently developing an app based on FCC Project Javascript Calculator. I decided it would use React and Redux.
I think I already finished most of the minimum code. It can be compiled. But when I click one of the buttons there is an error:
Error: Actions must be plain objects. Use custom middleware for async actions.
When I googled the message, most of the results are about using redux-thunk. I applied it without knowing how redux-thunk works. But it still gets an error.
From what I mean I applied is I add import thunk from 'redux-thunk'
and add applyMiddleware(thunk)
in the store. Nothing else.
I provide the App.js code below. What am I missing? Can I modify the code somewhere with or without redux-thunk? Or should I first learn about async await and something about that? Or from the first my logic is flawed?
Main code
import React, { Component } from 'react';
import { store } from './store';
import {
setFirstOperand,
setSecondOperand,
setOperator
} from './actions';
import './App.css';
class DisplayArea extends Component {
filterInputDisplay(firstOperand, operator, secondOperand) {
let result;
if(operator === "" && secondOperand === "0") {
result = firstOperand;
} else if(operator !== "" && secondOperand === "0") {
result = operator;
} else {
result = secondOperand;
}
return result;
}
filterResultDisplay(firstOperand, operator, secondOperand) {
let result = "";
result = "" + firstOperand;
if(operator !== "") {
result = result + " " + operator;
if(secondOperand !== "0") {
result = result + " " + secondOperand;
}
}
return result;
}
render() {
const firstOperand = store.getState().firstOperand.join('');
const operator = store.getState().operator;
const secondOperand = store.getState().secondOperand.join('');
const inputDisplay = this.filterInputDisplay(firstOperand, operator, secondOperand);
const resultDisplay = this.filterResultDisplay(firstOperand, operator, secondOperand);
return (
<div className="display-area">
<ResultArea text={resultDisplay} />
<IndividualInputArea text={inputDisplay}/>
</div>
);
}
}
class ResultArea extends Component {
render() {
return (
<div>
<p className="result-area">
{this.props.text}
</p>
</div>
);
}
}
class IndividualInputArea extends Component {
render() {
return (
<div>
<p className="individual-input-area">
{this.props.text}
</p>
</div>
);
}
}
class ButtonGroup extends Component {
render() {
const buttonData = [
{ id: "zero", text: "0" },
{ id: "one", text: "1" },
{ id: "two", text: "2" },
{ id: "three", text: "3" },
{ id: "four", text: "4" },
{ id: "five", text: "5" },
{ id: "six", text: "6" },
{ id: "seven", text: "7" },
{ id: "eight", text: "8" },
{ id: "nine", text: "9" },
{ id: "decimal", text: "." },
{ id: "percent", text: "%" },
{ id: "add", text: "+" },
{ id: "subtract", text: "-" },
{ id: "multiply", text: "x" },
{ id: "divide", text: "/" },
{ id: "equal", text: "=" },
{ id: "clear", text: "AC" },
{ id: "backspace", text: "<=" }
];
return (
<div className="button-group">
{buttonData.map((value, index) => (
<Button key={`btn-${index}`} buttonId={value.id} buttonText={value.text} />
))}
</div>
);
}
}
class Button extends Component {
isEmpty(operator) {
return operator === "";
}
hasDecimalTail(operand) {
return /\d+\./.test(operand);
}
isPercent(buttonPressed) {
return buttonPressed === "%";
}
isZero(operand) { // this can be operand or buttonPressed
return operand === "0";
}
isOnlyNumber(buttonPressed) {
return /\d/.test(buttonPressed);
}
hasDecimal(operand) {
return /\d+\.\d*/.test(operand);
}
isDecimal(buttonPressed) {
return buttonPressed === ".";
}
hasPercent(operand) {
return /\d+\%/.test(operand);
}
appendOperand(buttonPressed) {
let operand, newOperand;
let operator = store.getState().operator;
if(this.isEmpty(operator)) {
operand = store.getState().firstOperand.join('');
} else {
operand = store.getState().secondOperand.join('');
}
if(this.hasDecimalTail(operand) && this.isPercent(buttonPressed)) {
newOperand = operand.slice(0, -1) + buttonPressed;
} else if(this.isZero(operand) && this.isOnlyNumber(buttonPressed)) {
newOperand = buttonPressed;
} else if( ( this.isZero(operand) && this.isZero(buttonPressed) ) || ( this.hasDecimal(operand) && this.isDecimal(buttonPressed) ) || this.hasPercent(operand) ) {
newOperand = operand;
} else {
newOperand = operand + buttonPressed;
}
let newNewOperand = newOperand.split('');
if(this.isEmpty(operator)) {
store.dispatch(setFirstOperand(newNewOperand));
} else {
store.dispatch(setSecondOperand(newNewOperand));
}
}
isEmptyArray(operand) {
return operand === [];
}
addOperator(buttonPressed) {
let secondOperand = store.getState().secondOperand.join('');
let operator = store.getState().operator;
if(this.isEmptyArray(secondOperand)) {
let newSecondOperand = ["0"];
store.dispatch(setOperator(buttonPressed));
store.dispatch(setSecondOperand(newSecondOperand));
} else if( !(this.isEmpty(operator)) && !(this.isEmptyArray(secondOperand)) ) {
this.calculateAll();
store.dispatch(setOperator);
}
}
calculateAll() {
let firstOperand = store.getState().firstOperand.join('');
let secondOperand = store.getState().secondOperand.join('');
let operator = store.getState().operator;
let result;
switch(operator) {
case "+":
result = +firstOperand + +secondOperand;
break;
case "-":
result = +firstOperand - +secondOperand;
break;
case "x":
result = +firstOperand * +secondOperand;
break;
case "/":
result = +firstOperand / +secondOperand;
break;
default:
break;
}
let newFirstOperand = ("" + result).split('');
let newOperator = "";
let newSecondOperand = [];
store.dispatch(setFirstOperand(newFirstOperand));
store.dispatch(setOperator(newOperator));
store.dispatch(setSecondOperand(newSecondOperand));
}
clearAll() {
let newFirstOperand = ["0"];
let newOperator = "";
let newSecondOperand = [];
store.dispatch(setFirstOperand(newFirstOperand));
store.dispatch(setOperator(newOperator));
store.dispatch(setSecondOperand(newSecondOperand));
}
removeLastElement() {
let i = 0;
}
dispatchButtonAction(buttonPressed) {
switch(buttonPressed) {
case "zero":
case "one":
case "two":
case "three":
case "four":
case "five":
case "six":
case "seven":
case "eight":
case "nine":
case "decimal":
case "percent":
this.appendOperand(buttonPressed);
break;
case "add":
case "subtract":
case "multiply":
case "divide":
this.addOperator(buttonPressed);
break;
case "equal":
this.calculateAll();
break;
case "clear":
this.clearAll();
break;
case "backspace":
this.removeLastElement();
break;
default:
break;
}
}
render() {
return (
<button
id={this.props.buttonId}
type="button"
value={this.props.buttonText}
onClick={this.dispatchButtonAction.bind(this, this.props.buttonId)}
>
{this.props.buttonText}
</button>
);
}
}
class App extends Component {
render() {
return (
<div className="App simple-calc">
<DisplayArea />
<ButtonGroup />
</div>
);
}
}
export default App;