Decrement / increment logic in counter app with double digits - javascript

I have a counter / calculator app which takes the count value from multiple instances of a Counter component and combines them as the final result. The Counter component has an increment and a decrement button that add or subtract the count value in each instance. The value cannot be negative.
The problem I'm having is that the value is supposed to show as a double digit, meaning that the starting value is 00 and increments as 01, 02 until it reaches 10. My solution was to use a string literal to show a 0 in front of the value as long as the value is less than 10, but this is causing problems with my handleValue logic, and now the decrement button no longer responds.
Everything worked as it should before adding the string literal.
I cannot seem to figure out why the string literal is causing these problems or how to fix them. Any help would be greatly appreciated!
Code:
const Calculator = ({ onClick }) => {
const [values, setValues] = useState({
count1: 0,
count2: 0,
count3: 0,
});
const handleValue = (key, value) => {
if (!((values.count1 === 0) && (value === -1))) {
if (!((values.count2 === 0) && (value === -1))) {
if (!((values.count3 === 0) && (value === -1))) {
setValues((prevState) => ({ ...prevState, [key]: prevState[key] + value }));
}
}
}
};
return (
<div>
<Counter
id="count1"
value={values.count1 < 10 ? `0${values.count1}` : values.count1}
handleValue={handleValue}
/>
<Counter
id="count2"
value={values.count2 < 10 ? `0${values.count2}` : values.count2}
handleValue={handleValue}
/>
<Counter
id="count3"
value={values.count3 < 10 ? `0${values.count3}` : values.count3}
handleValue={handleValue}
/>
<button
type="button"
onClick={onClick}
>
SHOW RESULT
</button>
</div>
);
};
export default Calculator;

<Counter
id="count1"
value={values.count1 < 10 ? `0${values.count1}` : values.count1}
handleValue={handleValue}
/>
Here the type of value prop in Counter component isn't number. If you want to do some caculation, you'd better keep the type as number something like below
<Counter
id="count1"
value={values.count1}
handleValue={handleValue}
/>
So here what you need is to show the number starting with "0" if it is less than 10. In this case you can just make some trick on the visual side of the component.
Actually, I am not sure where you are showing value (maybe in Counter component?), but that's where you should use string iteral.

Related

How to update React properties after a delay in time and constantly sort an array to the nearest specific number X?

I'm working on a elevator simulator in React JS with typescript. It's quite simple, there are 6 buttons on every floor for the 6 floors to go to the chosen floor. And when you click on one of the buttons the color changes so you know it's activated. The elevator also got a Up and Down button to go 1+ or 1- but I already done that.
Here is the code for the floors. App.tsx
interface Floor {
floorNum: number;
}
const App: React.FC = () => {
// Current Clicked Buttons
const [clickedButtons, setClickedButtons] = useState<any>([]);
// All floors
const floors: Floor[] = [
{ floorNum: 5 },
{ floorNum: 4 },
{ floorNum: 3 },
{ floorNum: 2 },
{ floorNum: 1 },
{ floorNum: 0 },
];
// Current floor
const [currentFloor, setCurrentFloor] = useState<number>(0);
// Delay Floor Change in seconds
const [delayFloor, setDelayFloor] = useState<number>(1000);
return (
<div className="container">
<h1>React Programming Assignment</h1>
<div className="elevatorContainer">
{floors.map((floor: Floor, index: number) => {
const floorNumber = floor.floorNum;
return (
<div className="singleFloorContainer">
{currentFloor == floorNumber ? (
<React.Fragment>
<UpAndDown
setCurrentFloor={setCurrentFloor}
floorNumber={floorNumber}
delayFloor={delayFloor}
/>
<NumberBtns
clickedButtons={clickedButtons}
setClickedButtons={setClickedButtons}
setCurrentFloor={setCurrentFloor}
floors={floors}
/>
</React.Fragment>
) : (
<div></div>
)}
<div
className={
currentFloor == floor.floorNum
? "singleFloor current"
: "singleFloor"
}
>
<p>{floor.floorNum}</p>
</div>
</div>
);
})}
</div>
</div>
);
};
Here is the code for the floor buttons. NumberBtns.tsx
const handleClickButton = (floorNumber: number) => {
setClickedButtons((oldVal: any) => [...oldVal, floorNumber]);
};
return (
<div className="numberBtnsContainer">
{floors.map((floor: Floor) => {
const floorNumber = floor.floorNum;
return (
<p
onClick={() => {
handleClickButton(floorNumber);
}}
className={
clickedButtons.includes(floorNumber)
? "floorNumber include"
: "floorNumber"
}
>
{floor.floorNum}
</p>
);
})}
</div>
);
Questions:
So I have 2 questions.
1: How to make it when I click on multiple buttons at the same time the delay when switching floors is still the same.
2: How to make it that it always goes to the nearest floor
For example, when the elevator is on the fifth floor and you press the buttons 0, 1, and 3 in this order, the elevator must recognize that the most efficient order to stop is 3 -> 1 -> 0.
Calculate the distance from the starting floor to all the floors clicked and sort the array from closest to farthest.
clickedButtons.sort(function(a, b){
return Math.abs(floorNumber-a) - Math.abs(floorNumber-b);
});
Just to elaborate, it calculates the distance of two numbers from 1, i.e. for
a=-10 and b=4, the distances are 11 and 3 respectively. The function returns a positive number, so 4 comes before -10 in the sorted array.
For a=-1 and b=4, the distances would be 2 and 3, the function returns a negative number so -1 comes before 4 in the array.

Toggle 0 and 1 as boolean values in React

I'm making a toggle component in React and send the state to my database using LARAVEL.
When I'm sending the state of the toggle, in the database it's registered as 1 or 0, but when I'm activating/disabling the toggle, in the console.log it shows the values of true & false.
The problem here is that when I send the value to the database and I want to get it, the toggle doesn't recognize 0 and 1 because I'm writing a condition for if true or false, so I can't read the values registered on the db which are 0/1.
Is there a solution on how can I convert the received boolean to true/false ?
The hook and Handler :
const [showlogo, setshowlogo] = useState('');
const HideLogo = () => {
setshowlogo(!showlogo);
console.log(showlogo)
}
The toggle code :
<div onClick={HideLogo} >Toggle</div>
<div className={`${ showlogo ? "on" : "off" }`}>Valeur of logo</div>
If you want to toggle 0 and 1 as boolean values:
const { useState } = React
const response = {data: {showlogo: 0}}
function App() {
const [showlogo, setShowlogo] = useState(response.data.showlogo);
const hideLogo = () => {
setShowlogo(prev => prev === 0 ? 1 : 0); // <-- HERE
}
return (<div>
<div onClick={hideLogo} >Toggle (click), showlogo: {showlogo}</div>
<div className={showlogo===1?"on":"off"}> Valeur of logo</div>
</div>)
}
ReactDOM.render(<App />, document.body)
<script crossorigin src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
Other answers recommended using Boolean, but since your db returns 1 or 0, and sounds like you have no control over it, then alternative use === operator
showLogo === 1 ? 'on' : 'off'
and use the following to toggle between 1 and 0
setShowLogo( showLogo === 1 ? 0 : 1 )
const status = 0
console.log(Boolean(status === 0))
console.log(Boolean(status === 1))
// IF it's a string then use String
const stringStatus = '0'
console.log(Boolean(stringStatus === '0'))
console.log(Boolean(stringStatus === '1'))
if you are dealing with 1 and 0 coming from the database, you can cast them to boolean with Boolean(x) -
Boolean(0) // false
Boolean(1) // true
and then if you have to save them again as 0 or 1, convert them back to a number from a boolean -
const x = showLogo ? 1 : 0
// then send x back to the db
Your initial value should be boolean
import React from "react";
let count = 1;
export default function App() {
const [showlogo, setshowlogo] = React.useState(0);
const HideLogo = (id) => {
if (id % 2 !== 0) setshowlogo(1);
else setshowlogo(0);
count++;
};
return (
<div>
<button onClick={() => HideLogo(count)}>Toggle</button>
{showlogo && (
<div className={`${showlogo ? "on" : "off"}`}>Valeur of logo</div>
)}
</div>
);
}
Demo
Just pass your id received from DB
onClick={() => HideLogo(ID)}
and toggle here based on passed ID
const HideLogo = (id) => {
if (id=== 0) setshowlogo(1);
else setshowlogo(0);
};

Calculate New Value From Previous populated value from .map()

I am Making a Table in react Js using the .map()function. In the last <td> of the table, I am calculating the taxed amount of the product from a function. This amount fills in every row <td> specified for them.
{
(!cr.items.length < 1) &&
cr.items.map((item , ind) => {
return(
<td scope="col">{(item) ? taxedValue(item.quantity , item.salesInfo.unitPrice , item.discount , item.tax) :null}</td>
</tr>
)
})
}
Function taxedValue return a new amount. There can be many rows for this. What I want is to Get the sum of all the previously taxedValue rows.
Like When Map returns these row
<tr><td>1<td></td>
<tr><td>2<td></td>
I want to add {1+2} = 3(//get this as my total amount to access it anywhere in the code//}
I tried calling a state function with an initial value 0. But gets an error Too much re-render.
It would be easy to simply add totaling a sum in the array.prototype.map function, but this callback is to be a pure function without side-effects.
I would suggest just computing the total separately using array.prototype.reduce. If computing the taxed value twice per render cycle is prohibitive then refactor the code to compute the taxed value once and inject it into the data you want to map and add it to a total sum.
const computeTotalTaxedValue = cr.items.reduce(
(total, item) => item ? taxedValue(item.quantity, item.salesInfo.unitPrice, item.discount, item.tax) : 0,
0,
);
If computing the taxed value twice for each item twice per render cycle is prohibitive then compute it once and inject it into the data and compute a sum.
const [totalTaxedValue, setTotalTaxedValue] = useState(0);
const [taxedValues, setTaxedValues] = useState([]);
useEffect(() => {
let totalTaxedValue = 0;
const taxedValues = [];
cr.items.forEach(item => {
const taxValue = item ? taxedValue(item.quantity, item.salesInfo.unitPrice, item.discount, item.tax) : null;
totalTaxedValue += taxValue;
taxedValues.push(taxValue);
});
setTotalTaxedValue(totalTaxedValue);
setTaxedValues(taxedValues);
}, [cr.items]);
...
{
(!taxedValues.length < 1) &&
taxedValues.map((taxValue, ind) => <td scope="col">{taxValue}</td>
})
}
I think you can do that in a separate function to keep that logic isolated. What you can do is define a variable and increment the value in every iteration of the map and then, in the last element, render an additional column. Something like this:
const renderColumns = () => {
let total = 0;
if (!cr.items.length < 1) {
return cr.items.map((item , ind) => {
const value = item ? taxedValue(item.quantity , item.salesInfo.unitPrice , item.discount , item.tax) : null;
total += value || 0;
return (
<>
<td scope="col">{value}</td>
{ind === cr.items.length - 1 ? (
<td>{total}</td>
) : null}
</>
);
});
}
return null;
};

How do I properly handle 'NaN' value in input type='number' in React?

Just creating a simple currency converter (React + Typescript).
Everything works good. But I'm bit misunderstanding 1 thing: how to clear input (where you enter amount of money) dynamically properly?
Code:
const App = () => {
...
const [amount, setAmount] = useState(1)
const [amountInFromCurrency, setAmountInFromCurrency] = useState(true)
let fromAmount, toAmount
if (amountInFromCurrency) {
fromAmount = amount
toAmount = parseFloat((amount * exchangeRate).toFixed(2))
} else {
toAmount = amount
fromAmount = parseFloat((amount / exchangeRate).toFixed(2))
}
return (
<Card
amount={fromAmount}
/>
<Card
amount={toAmount}
/>
)
}
const Card = ({ amount }) => {
return (
<input type="number" value={amount || 0} onChange={changeInput}/>
)
}
When I start clean the input to the end it becomes 0 in both inputs as expected. But when I start entering the numbers in the input after cleaning it (when it shows 0) they start appearing after 0 but not instead. Example: shows "0", I start entering the numbers, shows "0123" instead "123". The 0 disapears only if I highlight all the numbers with cursor and enter another number or it disappears if I push inner/outer-spin-buttons in input.
As you can see my approach here for input value is value={amount || 0}. If I put only value={amount} the mistake that 'You have NaN in value' appears when I clean input to the end. I don't need that mistake.
What I need: cleaning inputs fully without mistake or if I show 0 after cleaning I need to disapper it correctly when I start entering other numbers like "123" but not "0123"
UPDATE:
Code for input change:
const handleFromAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setAmount(parseInt(e.target.value))
setAmountInFromCurrency(true)
}
const handleToAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setAmount(parseInt(e.target.value))
setAmountInFromCurrency(false)
}

Multiple validate functions are treated as OR and not as AND

I'm trying to follow the Field-Level Validation Example from the official website.
This is my renderInput function that I use to render the field.
const renderInput = ({
input,
label,
type,
meta
}) => (
<React.Fragment>
<input
{...input}
type={type}
/>
<pre>{JSON.stringify({meta}, null, 4)}</pre>
</React.Fragment>
)
This is how I call it:
<Field
name="title"
component={renderInput}
validate={[required, minLength(10)]}
type="text"
/>
These are my validation functions:
const required = value => {
console.log('required', !!(value && value.length > 0) ? undefined : 'Mandatory')
return !!(value && value.length > 0) ? undefined : 'Mandatory';
};
const minLength = min => value => {
console.log(`minLength(${min})`, !!(value && value.length < min) ? `More than ${min} characters please` : undefined);
return !!(value && value.length < min) ? `More than ${min} characters please` : undefined;
}
I am typing test on my input, on which:
if (required('test') && minLength(10)) // false
And what's happening is
if (required('test') || minLength(10)) // true
... because 1 of them is true, the validation is passing.
Shouldn't validate array be false if one of the elements is false?
Or am I seeing this wrong?
What happens when you do what they do in the docs - put const minLength10 = minLength(10) and put that in the array.
From their example, it seems each of the passed in functions in the validate array are just ran sequentially and the first that doesn't return undefined (if any) will define the meta.error for that field.

Categories

Resources