Assigning value from setState to a variable in React js component - javascript

In the middle of a slightly long component which is basically a split date input (3 inputs for DD/MM/YYYY) - I think my approach is correct for the most part, but I'm struggling with my getDate function. The idea is that the currentDate is passed into this function (whatever has been entered in the keyboard), like so:
const handleValueChange = (date?: string) => {
onValueChange(getDate(date));
};
In the getDate function I want to take the values from selectedDay, selectedMonth & selectedYear and assign them to variables so that they can then be joined using the .join method and a valid date like "28-08-2022" can be returned.
Can anyone advise on a best possible approach?
const getDate = (date?: string): string => {
if (date) {
/* need to take value for DD/ MM / YYYY and store them in
individual variables that can then be used in an array or
something similar with .join to return a
joined up date like "28-09-2022" */
}
return "";
};
const DateInput = ({
dateInputValue,
dateInputFormat,
className,
inFilterContext,
onChange,
onValueChange,
value = "",
type = "text",
readOnly = false,
disabled = false,
placeholder,
mindate,
maxdate,
...inputProps
}: Props) => {
const [intialDayValue, initialMonthValue, initialYearValue] =
dateInputValue.split("-");
const [selectedDay, setSelectedDay] = useState(intialDayValue || "");
const [selectedMonth, setSelectedMonth] = useState(initialMonthValue || "");
const [selectedYear, setSelectedYear] = useState(initialYearValue || "");
const handleValueChange = (date?: string) => {
onValueChange(getDate(date));
};
// handle date input change event - DAY
const handleDayChangeEvent = (e: SyntheticInputEvent<any>) => {
const currentDate = e.target.value;
setSelectedDay(currentDate);
handleValueChange(currentDate);
};
// handle date input change event - MONTH
const handleMonthChangeEvent = (e: SyntheticInputEvent<any>) => {
const currentDate = e.target.value;
setSelectedMonth(currentDate);
handleValueChange(currentDate);
};
// handle date input change event - YEAR
const handleYearChangeEvent = (e: SyntheticInputEvent<any>) => {
const currentDate = e.target.value;
setSelectedYear(currentDate);
handleValueChange(currentDate);
};
return (
<StyledInputGroup>
<label htmlFor={`${name}_day`}>
<span>Day</span>
<StyledInput
{...inputProps}
type={type}
maxLength="2"
disabled={disabled}
readOnly={readOnly}
value={selectedDay}
onChange={handleDayChangeEvent}
onValueChange={handleDateSelectDay}
/>
</label>
<label htmlFor={`${name}_month`}>
<span>Month</span>
<StyledInput
{...inputProps}
type={type}
maxLength="2"
disabled={disabled}
readOnly={readOnly}
value={selectedMonth}
onChange={handleMonthChangeEvent}
onValueChange={handleDateSelectMonth}
/>
</label>
<label htmlFor={`${name}_year`}>
<span>Year</span>
<StyledInput
{...inputProps}
type={type}
maxLength="4"
disabled={disabled}
readOnly={readOnly}
value={selectedYear}
onChange={handleYearChangeEvent}
/>
</label>
</StyledInputGroup>
);

Related

How to display a set number of jsx elements depending on number placed in an input field. React

I have an input field that takes in a number.(between 1 and 30) I want to display an array of items depending on what number is placed in that text field. how can this been done with React hooks. I have something basic for a start like this, but this might not even be the best way to start this.
export default function App() {
const [state, setState] = React.useState({ value: "" });
const [myArray, updateMyArray] = React.useState([]);
const onSubmit = () => {
updateMyArray((arr) => [...arr, `${state.value}`]);
};
const handleChange = (event) => {
let { value, min, max } = event.target;
value = Math.max(Number(min), Math.min(Number(max), Number(value)));
setState({ value });
};
return (
<>
<input
type="number"
onChange={handleChange}
value={state.value}
min={""}
max={100}
/>
<button onClick={onSubmit}>Confirm</button>
{state.value && (
<>
<div>
{myArray?.map((e) => (
<div>{e}</div>
))}
</div>
</>
)}
</>
);
}
You can do it like this
updateMyArray(new Array(state.value).fill(""));
This will create a new array with the length of state.value and asign it to myArray
Maybe this example will be helpful for you.
function App() {
const [amount, setAmount] = useState(0);
const [submittedAmount, setSubmittedAmount] = useState(0);
// optionally
const onSubmit = () => {
setSubmittedAmount(amount);
};
const handleChange = (event) => {
let { value, min, max } = event.target;
value = Math.max(Number(min), Math.min(Number(max), Number(value)));
setAmount(value);
};
return (
<>
<input
type="number"
onChange={handleChange}
value={amount}
min={0}
max={100}/>
<button onClick={onSubmit}>Confirm</button>
{ /* you can use amount instead of submitted amount if you want */
{submittedAmount > 0 && Array.from({ length: submittedAmount }, (_, index) => <div key={index}>{index}</div> )}
</>
);
}
In my opinion if you can skip submitting and use only amount state. Thanks to this your UI will change automatically after input value change without submitting.
If you know the value of value, you can loop till that number, before the render, like:
const items = [];
for (let i; i < state.value; i++) {
items.push(<div>{i}</div>);
}
return (
<div>
{items}
</div>
)

React: Infinite function call loop after Slider onChangeEnd

When I change the value of my slider, it should call a function onChangeEnd. However, when this occurs, the calculateEvent function is called in an infinite loop. This continues forever, even as I keep moving my slider around afterwards. Why does this happen?
var yearStart = 0;
var yearEnd = 50;
var years : number[] = [];
while(yearStart < yearEnd+1){
years.push(yearStart++);
}
const xAxis : number[] = years;
const Savings = () => {
const [initialSavings, setInitialSavings] = React.useState<string>("5000");
const [monthlyDepo, setMonthlyDepo] = React.useState<string>("100");
const [interestRate, setInterestRate] = React.useState<number>(2);
const [yAxis, setYAxis] = React.useState<number[]>([]);
const handleChange = useCallback((newValue) => {
setInterestRate(newValue);
}, []);
const calculateEvent = useCallback((event : string, slider : number, option : number) => {
switch(option) {
case 1:
setInitialSavings(event);
break;
case 2:
setMonthlyDepo(event);
break;
case 3:
setInterestRate(slider);
}
console.log("Calculate event called with slider value: ", interestRate);
getProjection(initialSavings, monthlyDepo, interestRate).then((m) => {
console.log(m);
setYAxis(m);
})
}, [initialSavings, monthlyDepo, interestRate]);
return(
<DefaultLayout>
<Container pt={6}>
<VStack spacing={4}>
<Heading as="h1">Interest Rate Calculator</Heading>
<Heading as="h4">{initialSavings}, {monthlyDepo}, {interestRate}</Heading>
<Input
label="Initial Savings amount"
name="Initial Savings"
onInput={e => calculateEvent(e.currentTarget.value, 0, 1)}
placeholder="5000"
/>
<Input
label="Monthly Deposit"
name="Monthly Deposit"
onInput={e => calculateEvent(e.currentTarget.value, 0, 2)}
placeholder="100"
/>
<Slider
label="Interest Rate"
name="Interest Rate"
value={interestRate}
min={1}
max={15}
step={1}
onChange={handleChange}
onChangeEnd={e => calculateEvent("", e, 3)}
focusThumbOnChange={false}
role="group"
onFocus={(e) => {
e.preventDefault();
}}
/>
<LineChart
title="Savings Over time"
xAxisData={xAxis}
yAxisData={yAxis}
xLabel="Years"
yLabel="Amount"
/>
</VStack>
</Container>
</DefaultLayout>
)
}
Okay, heres what I propose.
Let's break that calculateEvent function up into its separate components.
Then, let's create a useEffect callback that will re-calculate the projection, whenever our inputs change. I'd prefer to do this on render, but we are stuck with an effect because getProjection returns a promise.
Now, I understand why you want to use the onChangeEnd event. getProjection() is expensive, and so you only want to call it, when the user has finished choosing a value.
So let's break it up into two states. One state to manage the slider value. And then a second state to hold the actual interestRate value. We will wait to update the interestRate value until onChangeEnd is called.
Also, let's make sure to control those inputs by putting values on them, and using onChange instead of onInput.
var yearStart = 0;
var yearEnd = 50;
var years: number[] = [];
while (yearStart < yearEnd + 1) {
years.push(yearStart++);
}
const xAxis: number[] = years;
const Savings = () => {
const [initialSavings, setInitialSavings] = React.useState<string>('5000');
const [monthlyDepo, setMonthlyDepo] = React.useState<string>('100');
const [interestRate, setInterestRate] = React.useState<number>(2);
const [interestRateSlider, setInterestRateSlider] = React.useState<number>(2);
const [yAxis, setYAxis] = React.useState<number[]>([]);
const handleSavingsChange = (e) => {
setInitialSavings(e.target.value);
}
const handleMonthlyDepoChange = (e) => {
setMonthlyDepo(e.target.value);
}
const handleInterestRateSliderChange = (value) => {
setInterestRateSlider(value);
};
const handleInterestRateSliderChangeEnd = (value) => {
setInterestRate(value)
}
useEffect(() => {
getProjection(initialSavings, monthlyDepo, interestRate).then((m) => {
setYAxis(m);
});
},[initialSavings, monthlyDepo, interestRate])
return (
<DefaultLayout>
<Container pt={6}>
<VStack spacing={4}>
<Heading as="h1">Interest Rate Calculator</Heading>
<Heading as="h4">
{initialSavings}, {monthlyDepo}, {interestRate}
</Heading>
<Input
label="Initial Savings amount"
name="Initial Savings"
value={initialSavings}
onChange={handleSavingsChange}
placeholder="5000"
/>
<Input
label="Monthly Deposit"
name="Monthly Deposit"
value={monthlyDepo}
onChange={handleMonthlyDepoChange}
placeholder="100"
/>
<Slider
label="Interest Rate"
name="Interest Rate"
value={interestRate}
min={1}
max={15}
step={1}
onChange={handleInterestRateSliderChange}
onChangeEnd={handleInterestRateSliderChangeEnd}
focusThumbOnChange={false}
role="group"
onFocus={(e) => {
e.preventDefault();
}}
/>
<LineChart
title="Savings Over time"
xAxisData={xAxis}
yAxisData={yAxis}
xLabel="Years"
yLabel="Amount"
/>
</VStack>
</Container>
</DefaultLayout>
);
};

ReactJS: one digit input can end up with multiple '0's

Basically, I want a little box with only one digit, it "works" fine until you try to put multiple '0', then in the box, multiple 0000 persist.
The code
const InputBox = () => {
const [value, setValue] = useState(0);
const handleChange = el => {
const newValue = el.target.value;
console.log(newValue);
const lastNumber = parseInt(
newValue.toString().slice(-1),
);
console.log(lastNumber);
setValue(lastNumber);
};
return (
<input
type='number'
pattern='[0-9]'
min='0'
max='9'
id='numberInput'
maxLength={1}
minLength={1}
value={value}
onInput={handleChange}
// onChange={handleChange}
className='inputBox'
/>
);
};
export default InputBox;
How to make it so I have only one digit at any time?
Don't need to parse to int or to string, just slice and set value.
minLength and maxLength doesn't work in <input type="number">.
const InputBox = () => {
const [value, setValue] = useState(0);
const handleChange = (el) => {
let newValue = el.target.value;
if (newValue.length > 1) {
newValue = newValue.slice(0, 1);
}
setValue(newValue);
};
return (
<input
type="number"
pattern="[0-9]"
min="0"
max="9"
id="numberInput"
value={value}
onInput={handleChange}
className="inputBox"
/>
);
};
This could work out
const handleChange = el => {
const newValue = el.target.value;
const lastNumber = newValue.replace(/[^0-9]/g,'')
setValue(lastNumber);
};
return (
<input
type="text" maxlength="1"
value={value}
onInput={handleChange}
/>
);
If you don't mind the type coercion to string of your state (you can always Number(value) when you need a number type for computations) then you can simply slice off the last char of the value and if it ends up being a falsey ('', 0, null, undefined, NaN) value fallback to 0 to allow coercion back to "0" when assigning to the value prop of the input. Also, the minLength|maxLength|pattern attributes only work when doing field validation in a form.
const handleChange = (el) => {
const { value } = el.target;
setValue(value.slice(-1) || 0);
};
const InputBox = () => {
const [value, setValue] = useState(0);
const handleChange = (el) => {
const { value } = el.target;
setValue(value.slice(-1) || 0);
};
return (
<input
type="number"
min="0"
max="9"
id="numberInput"
value={value}
onChange={handleChange}
className="inputBox"
/>
);
};

How to use toFixed properly while entering input value

I'd like to keep the two digits after a number ie 2.89 or 2.00. Google brought me to this answer to use .toFixed(2).
While that works great, it does not work well when entered input values:
const [ value, setValue] = useState()
const onChange = (value) => {
const float = parseFloat(value)
setValue(float.toFixed(2))
}
<input
type="number"
value={value}
onChange={({ target }) => onChange(target.value)}
/>
If I should type, say, "300", the input value stops at "3.00". I have to move the cursor before "3" to type "300". What's the best way to do this?
I expect the value to always show .33, .00 etc while having the ability to "free type". As I type this question, I feel I need to use onBlur to convert the value to .toFixed and not while typing?
You can use onBlur and add some checks in while setting value
export default function App() {
const [value, setValue] = useState('');
const onChange = (v) => {
if (!Number.isNaN(v)) {
setValue(v);
}
};
return (
<div className="App">
<input
type="number"
value={value}
step="1"
onChange={({ target }) => onChange(target.value)}
onBlur={e => setValue(Number(value).toFixed(2))}
/>
</div>
);
}
I would not try to set the decimal places on the number on the onChange but make an onBlur handler.
const TodoApp = ( ) => {
const [ value, setValue] = React.useState('');
const onBlur = (e) => {
const float = parseFloat(e.target.value)
setValue(float.toFixed(2))
}
return (
<input
type="number"
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={onBlur}
/>
);
}
Hi dude read this https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number
And try using step propety, i recommended
<input
type="number"
value={value}
step="1"
onChange={({ target }) => onChange(target.value)}
/>
and try if it works
export const InputElement = () => {
const [value, setValue] = React.useState(0);
const fixInt = (v) => {
setValue(Number(v).toFixed(2));
};
return (
<div className="App">
<input
type="number"
value={value}
step="1"
onChange={({ target }) => fixInt(target.value)}
/>
</div>
);
}

Getting the date from a Bootstrap React form

How do I extract a yyyy-mm-dd timestamp from the following?
handleDateChange = e => {}
<Form.Group as = {Col}>
<Form.Control
type = "date"
value = { this.state.closingDate }
onChange = { this.handleDateChange }
/>
</Form.Group>
At the moment e.timeStamp gives me a four or five digit number.
Try this:
handleDateChange = e => {
const date = new Date(e.target.value);
const timestamp = date.getTime();
console.log(timestamp)
this.setState({
closingDate: e.target.value
})
}

Categories

Resources