react,js calculator not concatenating numbers - reactjs

import React, { useState } from 'react';
const Calculator = () => {
const [displayValue, setDisplayValue] = useState('0');
const [operations, setOperations] = useState([]);
const handleClick = (e) => {
const value = e.target.getAttribute('data-value');
if (value === 'C') {
setDisplayValue('0');
setOperations([]);
} else if (value === 'CE') {
setDisplayValue('0');
setOperations(operations.slice(0, -1));
} else if (value === '=') {
const result = eval(operations.join(''));
setDisplayValue(result);
setOperations([]);
} else if (!isNaN(value)) {
// Handle number buttons
setDisplayValue(displayValue === '0' ? value : displayValue + value);
} else {
// Handle operation buttons
if (displayValue !== '') {
setOperations([...operations, displayValue, value]);
setDisplayValue('');
}
}
};
const renderButtons = () => {
const buttons = [
['7', '8', '9', '+'],
['4', '5', '6', '-'],
['1', '2', '3', '/'],
['.', '0', 'C', '=']
];
return buttons.map((row) => {
return (
<div className="button-row">
{row.map((button) => {
return (
<button
key={button}
data-value={button}
onClick={handleClick}
>
{button}
</button>
);
})}
</div>
);
});
};
return (
<div className="calculator">
<div className="display">{displayValue}</div>
{renderButtons()}
</div>
);
};
export default Calculator;
This is my code for a simple calculator using react.js
The problem is when I digit a number, for exemple "40", the display only shows 4, and than 0, never 4 and 0 together, although the final result after= is correct.
40+50=90.
But when I digit it only shows 4, than 0, than 5, than 0. One at the time.
How to fix it?
I tried several ways to fix it without good results.

You were not adding the last number that was displaying before clicking the = sign. I've added some conditions to add the last display value
import React, { useState } from "react";
const Calculator = () => {
const [displayValue, setDisplayValue] = useState("0");
const [operations, setOperations] = useState([]);
const handleClick = (e) => {
e.preventDefault();
const value = e.target.getAttribute("data-value");
if (value === "C") {
setDisplayValue("0");
setOperations([]);
} else if (value === "=") {
console.log(operations);
if (operations.length === 0) {
setDisplayValue("0");
} else {
const result = eval(operations.join("") + displayValue);
setDisplayValue(result);
setOperations([]);
}
} else if (!isNaN(value)) {
// Handle number buttons
console.log("num", value);
setDisplayValue(displayValue === "0" ? value : displayValue + value);
} else {
// Handle operation buttons
if (displayValue !== "") {
setOperations([...operations, displayValue, value]);
setDisplayValue("");
}
}
};
console.log("num", displayValue);
console.log("op array", operations);
const renderButtons = () => {
const buttons = [
["7", "8", "9", "+"],
["4", "5", "6", "-"],
["1", "2", "3", "/"],
[".", "0", "C", "="]
];
return buttons.map((row) => {
return (
<div className="button-row">
{row.map((button, index) => {
return (
<button key={index} data-value={button} onClick={handleClick}>
{button}
</button>
);
})}
</div>
);
});
};
return (
<div className="calculator">
<div className="display">{displayValue}</div>
{renderButtons()}
</div>
);
};
export default Calculator;
also I've removed the CE operation as it was serving no purpose

Related

How to combine useState hook with switch cases in react

Here is component with the array of items, counters val and spiceValue and rendering block with adding some quantity of spices for example. Every spice has own price, adding or removing by click on plus or minus. How to implement this logic according to best practices of react?
export const SpiceBlock = ({ isCalculated }) => {
const [spiceQuantity, setSpiceQuantity] = useState(0);
var val = 0;
var spiceValue = 0;
Calling useEffect with passed val as argument in any part of this code could not to read val
useEffect((val) => {
setSpiceQuantity();
}, []);
const spicesCount = (event) => {
const direction = event.target.dataset.direction;
const dataSpice = event.target.dataset.spice;
switch (dataSpice) {
case "guarana":spiceValue = 21;break;
case "zhen":spiceValue = 11;break;
case "cinnamon":spiceValue = 33;break;
case "tapioka":spiceValue = 41;break;
default:return false;
}
if (direction === "plus") {
val += spiceValue;
} else if (val - spiceValue > 0) {
val -= spiceValue;
}
};
const spicesArr = [
{ name: "Guarana", data: "guarana" },{ name: "Zhenshen", data: "zhen" },
{ name: "Cinnamon", data: "cinnamon" },{ name: "Tapioka", data: "tapioka" },
];
return (
<div className={`spiceItems ${isCalculated ? "calculated" : ""}`}>
{isCalculated && spicesArr
? spicesArr.map((spice, index) => (
<div className="counter" key={`spice${index}`}>
<button
className="counter__btn"
data-direction="minus"
data-spice={spice.data}
onClick={spicesCount}
>
-
</button>
<label htmlFor="spice" type="text">
{spice.name}
<input
type="text"
name="spice"
value={val}
disabled
className="counter__value"
/>
{spiceQuantity}
</label>
<button
className="counter__btn"
data-direction="plus"
data-spice={spice.data}
onClick={spicesCount}
>
+
</button>
</div>
))
: null}
</div>
);
};
Set val as a state and add it as a dependency to your useEffect. So that every time you set the value of val the setSpiceQuantity function will be triggered
const [val, setVal] = useState(0);
useEffect(() => {
setSpiceQuantity();
}, [val]);
if (direction === "plus") {
setVal(val += spiceValue)
} else if (val - spiceValue > 0) {
setVal(val -= spiceValue)
}
Any variable that is not a state will disappear after rerender, it's better to store variables in the state
const [value, setValue] = useState()
const getValue = type => {
switch (dataSpice) {
case "guarana": return 21;
case "zhen": return 11;
case "cinnamon": return 33;
case "tapioka": return 41;
}
}
const spicesCount = (event) => {
const direction = event.target.dataset.direction;
const dataSpice = event.target.dataset.spice;
const val = getValue(dataSpice);
if (direction === "plus") {
setValue(value => value + spiceValue)
} else if (val - spiceValue > 0) {
setValue(value => value - spiceValue)
}
};
<input
type="text"
name="spice"
value={value}
disabled
className="counter__value"
/>

AnimatedFlatList jumped to the first of list when fetchNextPage called

In onEndReached event I check for next page and if it exits I call fetchNextPage() of useInfiniteQuery.
but just after that my FlatList jumped into the first of the list.
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
return (
<AnimatedFlatList
{...props}
onScroll={onScroll({y}, {height}, {height: layoutH})}
onEndReached={({distanceFromEnd}) => {
if (distanceFromEnd < 0) {
return;
}
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}}
/>
);
and here is my useInfiniteQuery code:
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery(
[gate.getDiscover2.name, request],
({pageParam = 1}) =>
gate.getDiscover2({...request, discover_page: pageParam}),
{
getNextPageParam: lastPage => {
const {current_page, last_page} = lastPage.data || {};
return current_page < last_page ? current_page + 1 : false;
},
},
);

react ref is trying to reference a previously rendered dom

function App() {
const [array, setArray] = useState(null);
const refs = useRef(null);
console.log(array);
console.log(refs.current);
const onToggle = () => {
setArray((array) => {
if (array == null) {
refs.current = [{}];
return [{ input1: "", input2: "" }];
} else {
refs.current = null;
//refs.current = [{}];
return null;
}
});
};
const onAdd = () => {
setArray(array => {
if (array.length === 2) return array;
refs.current.push({});
return [...array, { input1: "", input2: "" }];
})
}
const onDelete = (i) => {
setArray((preArray) => {
const array = [...preArray];
array.splice(i, 1);
refs.current.splice(i, 1);
return array;
})
}
return (
<>
<div>
<button onClick={onToggle}>{array ? "off" : "on"}</button>
{array && <button onClick={onAdd}>add</button>}
</div>
{
array && (
array.map((arr,i) => (
<div key={i}>
<input ref={el => refs.current[i]["input1"] = el}/>
<input ref={el => refs.current[i]["input2"] = el}/>
<button onClick={() => onDelete(i)}>delete</button>
</div>
))
)
}
</>
)
}
I'm trying to give a ref to an input that is created and cleared dynamically and give it focus if the validity is invalid.
So, pressing the add button adds one to the ref array, and pressing the delete button deletes one from the ref array.
When the on button is pressed, it becomes as follows.
array: [{input1: '', input2: ''}]
refs: [{input1: input, input2: input}]
I thought it would be something like this when I hit the delete button.
array: []
refs: []
However, the actual value becomes:
array: []
refs: [{input1: null, input2: null}]
Why do refs have values ​​even when not rendering?

useState with an argument in it's array is breaking a setInterval and makes it glitchy and erratic

I just asked a question earlier here: react value of a state variable different in a different function
and now I have a new problem.
having a useEffect that looks like this
useEffect(() => {
countDown();
console.log('Score in useeffect', strokeScore);
}, [strokeScore]);
is breaking a setInterval that looks like this:
const countDown = () => {
// let strokeCountdown = Math.floor(Math.random() * 31) + 100;
let strokeCountdown = 20
let strokeCountdownSpeedOptions = [1000, 500, 300, 200];
let strokeCountDownSpeed = strokeCountdownSpeedOptions[Math.floor(Math.random()*strokeCountdownSpeedOptions.length)];
let strokeCounter = setInterval(() => {
strokeCountdown--
setStrokeCountdown(strokeCountdown)
if (strokeCountdown === 0) {
endOfGameRound()
clearInterval(strokeCounter)
setTotalStrokeScore(strokeScore);
}
}, strokeCountDownSpeed)
}
The full component looks like this:
import React, { useEffect, useState } from 'react';
function ScoreCard() {
const [strokeScore, setStrokeScore] = useState(1);
const [totalStrokeScore, setTotalStrokeScore] = useState(1);
const [strokeCountdown, setStrokeCountdown] = useState();
const strokeCountdownDing = new Audio('/sounds/round-complete.mp3');
// make new variable, maybe?
let strokeScoreCount = 0;
const endOfGameRound = () => {
strokeCountdownDing.play();
document.getElementById('stroke-counter-button').disabled = true;
}
const addToStrokeScore = () => {
setStrokeScore(prev => prev + 1);
// prints the correct number
console.log('Score in function', strokeScore);
if (strokeCountdown === 0) {
endOfGameRound()
}
}
const subtractStrokeScore = () => {
setStrokeScore(strokeScore - 1);
}
const countDown = () => {
// let strokeCountdown = Math.floor(Math.random() * 31) + 100;
let strokeCountdown = 20
let strokeCountdownSpeedOptions = [1000, 500, 300, 200];
let strokeCountDownSpeed = strokeCountdownSpeedOptions[Math.floor(Math.random()*strokeCountdownSpeedOptions.length)];
let strokeCounter = setInterval(() => {
strokeCountdown--
setStrokeCountdown(strokeCountdown)
if (strokeCountdown === 0) {
endOfGameRound()
clearInterval(strokeCounter)
setTotalStrokeScore(strokeScore);
}
}, strokeCountDownSpeed)
}
useEffect(() => {
countDown();
console.log('Score in useeffect', strokeScore);
}, [strokeScore]);
return (
<div className="game__score-card">
<div className="game__speed-level">
Speed: idk
</div>
<div className="game__stroke-countdown">
Countdown: {strokeCountdown}
</div>
<p>Score: {strokeScore}</p>
<button id="stroke-counter-button" onClick={addToStrokeScore}>
{strokeCountdown === 0 ? 'Game Over' : 'Stroke'}
</button>
{/* window.location just temp for now */}
{strokeCountdown === 0
? <button onClick={() => window.location.reload(false)}>Play Again</button>
: <button disabled>Game in Progress</button>
}
<div className="game__total-score">
Total score: {totalStrokeScore}
</div>
</div>
);
}
export default ScoreCard;
When I click on the button, the timer gets erratic and goes all over the place.
All I want to do is make it so that the timer counts down smoothly, gets the clicks the user made and add it to total score.
Why is
useEffect(() => {
countDown();
console.log('Score in useeffect', strokeScore);
}, [strokeScore]);
Breaking everything?
I was calling countDown() everytime I clicked so I just did
useEffect(() => {
if (strokeScore === 1) {
countDown();
}
console.log('Score in useeffect', strokeScore);
}, [strokeScore]);

React , custom hook slow render

Hello I made a custom hook that goes hand in hand with a component for generic forms, however, I notice that it is slow when the state changes.
#customHook
export const useFormController = (meta) => {
const { setVisible, setLoading } = useContext(FeedBackContext);
const itemsRef = useRef([]);
const {
control,
setValue,
handleSubmit,
formState: { errors },
} = useForm<Partial<any>>({
mode: "onBlur",
shouldUnregister: true,
resolver: yupResolver(meta.validation),
});
const onRef = function (input) {
this.itemsRef.current[this.index] = input;
};
const onSubmit = (data: any) => {
if(meta.onSubmit){
meta.onSubmit(data);
}else{
setVisible(true);
setLoading(true);
meta.service.submit(data);
}
};
const isJsonEmpty = (val = {}) => {
return Object.keys(val).length == 0;
};
const onSubmitIditing = function () {
let index = ++this.index;
if (isJsonEmpty(errors) && this.end) {
handleSubmit(onSubmit)();
} else if (!this.end) {
this.itemsRef.current[index]._root.focus();
}
};
const setFields = (json) => {
const items = Object.keys(json);
const values = Object.values(json)
console.log('Cambiando fields en formControllser...', json)
for (let i = 0; i < items.length; i++) {
//console.log('Cambiando valores...', items[i], values[i])
setValue(items[i], values[i], { shouldValidate: true })
}
}
const getItems = () => {
console.log('Meta namess', meta.names, meta);
if (!meta && !meta.names) return [];
return meta.names.map(function (item, index) {
const isEnd =
meta.options && meta.options[item] && meta.options[item].end
? true
: false;
const isSecure =
meta.options && meta.options[item] && meta.options[item].secure
? true
: false;
const label = meta.alias ? meta.alias[item] : item;
const visible = meta.invisibles ? (meta.invisibles[item] ? false : true) : true;
const def = meta.defaults ? meta.defaults[item] : "";
const disabled = (val) => {
const b = meta.disableds ? (meta.disableds[item] ? true : false) : false;
return b;
}
return {
name: item,
label: label,
disabled: disabled,
onRef: onRef.bind({ itemsRef: itemsRef, index: index }),
onSubmitEditing: onSubmitIditing.bind({
itemsRef: itemsRef,
index: index,
end: isEnd,
errors: errors,
}),
visible: visible,
setFields,
defaultValue: def,
errors: errors,
secureTextEntry: isSecure,
styles: styles,
control: control,
options: meta.options[item] ? meta.options[item] : null,
};
});
}
const getData = useMemo(() => {
console.log('Get data calback v2', meta);
return {
handleSubmit,
items: getItems(),
onSubmit,
errors,
setFields
};
}, [meta])
return getData;
};
export const Client: React.FC<any> = React.memo(({ navigation, route }) => {
const {
alias,
defaults,
ubigeoSeleccionado,
setUbigeoSeleccionado,
editable,
inputLabel,
search,
getDisabled,
getInvisibles,
getAlias,
getDefaults,
disableds,
invisibles,
searchVisible,
idTypeDocument,
currentTypeDocument,
allTypeDocuments,
onValueChange,
onChangeText } = useContext(CreateClientContext);
const [mode, setMode] = useState(() => {
return route?.params?.mode;
})
const [client, setClient] = useState(() => {
return route?.params?.client;
})
const { dispatchClient } = useContext(GlobalContext);
const clientService = useClientService();
const ref = useRef(0);
const options = useMemo(() => {
return {
names: ["ane_numdoc", "ane_razsoc", "ane_alias", "ane_email", "ane_tel", "ane_tel2", "ane_dir"],
validation: clientValidation,
alias: alias,
defaults: defaults,
disableds: disableds,
service: {
submit: (data) => {
const parse = { ...data, ubigeo_id: ubigeoSeleccionado.ubi_id, ane_tipo_cp: 2, ane_tipdoc: currentTypeDocument.id }
if (mode == "update") {
//console.log('Actualizando...', client.id, parse);
clientService.updateById(client.id, parse)
.then(ok => {
Alert.alert('Actualizaciòn de cliente', "Cliente Actualizado")
dispatchClient({
type: 'create',
payload: ok
});
setTimeout(() => {
navigation.navigate('App', {
screen: "Clients"
})
}, 500)
}).catch(e => {
Alert.alert('Actualizaciòn de cliente', "No se pudo actualizar")
})
} else {
clientService.create(parse)
.then(ok => {
dispatchClient({
type: 'create',
payload: ok
});
Alert.alert('Cliente', "Cliente creado")
setTimeout(() => {
navigation.navigate('App', {
screen: "Clients"
})
}, 500)
})
.catch(e => {
(e);
Alert.alert('Error', "No se pudo crear el cliente")
})
}
}
},
invisibles: invisibles,
options: {
ane_dir: {
end: true
},
}
}
}, [getDisabled,
getInvisibles,
getAlias,
getDefaults])
const { items, handleSubmit, onSubmit, errors, setFields } = useFormController(options);
useEffect(() => {
ref.current++;
})
useEffect(() => {
if (route.params) {
console.log('Ref current', ref.current);
setMode(route.params.mode);
setClient(route.params.client);
}
}, [route.params])
useEffect(() => {
// console.log('Mode', mode, client.id);
if (mode == "update" && client) {
console.log('cambiando fields'), ref;
setFields(client)
}
}, [mode, client])
// useEffect(()=>{
// },[instanceDocument])
useEffect(() => {
console.log('Cambiando cliente...', mode, client);
console.log(ref.current);
}, [client])
useEffect(() => {
//Creación
console.log('set defaults..', ref.current);
if (Object.keys(defaults).length > 0) {
setFields(defaults)
}
}, [getDefaults])
console.log('Current', ref.current);
return (
<StyleProvider style={getTheme(material)}>
<Container style={{ backgroundColor: "#FAF9FE" }}>
<Content style={GlobalStyles.mainContainer}>
<Text style={GlobalStyles.subTitle}>Cliente</Text>
<PickerSearch
search={search}
editable={editable}
style={styles}
searchVisible={searchVisible}
placeholder={inputLabel}
pickerItems={allTypeDocuments}
onValueChange={onValueChange}
selectedValue={idTypeDocument}
onChangeText={onChangeText}
></PickerSearch>
<FormListController
// top={<Top />}
items={items}
style={GlobalStyles}
></FormListController>
<Bottom
ubigeoSeleccionado={ubigeoSeleccionado}
setUbigeoSeleccionado={setUbigeoSeleccionado}
onSubmit={handleSubmit(onSubmit)}
/>
</Content>
<AppFooter2 navigation={navigation} />
</Container>
</StyleProvider>
);
});
export const FormListController: React.FC<any> = React.memo(({ children, items = [], style, top = null, bottom = null }) => {
console.log('%c Form list controlllser...', "background-color:#ccc");
console.log('items', items)
return (
<>
<Form style={!!style.form ? style.form : style.formContainer}>
{top}
{items.map((item: any, index) => {
return <FormItemController {...item} key={index} />;
})}
{bottom}
</Form>
</>
);
});
export const FormItemController: React.FC<any> = React.memo((props: any) => {
console.log('Form item controller print', props)
if (props.visible) {
return (
<>
<Controller
control={props.control}
render={
({ field: { onChange, onBlur, value } }) => {
return (
<Item regular style={props.styles.item}>
<Label style={props.styles.label}>{props.label}</Label>
<Input
onBlur={onBlur}
disabled={props.disabled(value)}
onChangeText={(value) => onChange(value)}
secureTextEntry={props.secureTextEntry}
onSubmitEditing={props.onSubmitEditing}
value={value}
ref={props.onRef}
/>
</Item>
)
}
}
defaultValue={props.defaultValue}
name={props.name}
/>
{props.errors && props.errors[props.name] && (
<TextError value={props.errors[props.name].message} />
)}
{/* {props.options && props.options.errorEmpty && props.errors[""] && (
<TextError value={props.errors[""].message} />
)} */}
</>
);
}
else {
return <></>
}
});
I use the same component to create and edit a client, but when editing and viewing the FormItemController the logs time span is less than 1 second, however it is not rendered until after 8 or 10 seconds.
This is the output of my logs.
Update cliente... 500ms
set defaults.. 77
Client num render 77
Client num render 78
Client num render 79
Client num render 80
Client num render 81
Client num render 82
Client num render 83
Client num render 84
Client num render 85
Client num render 86
Client num render 87
Client num render 88
Client num render 89
Client num render 90
Client num render 91
Client num render 92
Client num render 93
Client num render 94
Client num render 95
Client num render 96
Client num render 97
Client num render 98
Client num render 99
Client num render 100 (6-8 seg)
the problem I have is when I edit, when I use the forms to create I have no problems, I do not find the bottleneck to improve and prevent it from being slow.
After trying several options, I realized that when passing the setValue, I was sending several nulls and objects that did not go with the form, filtering this data, made the final rendering pass from 8 seconds to less than 1 second
const setFields = (json) => {
const items = Object.keys(json);
const values = Object.values(json)
for (let i = 0; i < items.length; i++) {
if (!!values[i] && typeof values[i] != 'object') {
setValue(items[i], values[i])
}
}
}

Resources