Return the default value in the TextField - javascript

There are several filters on my site (by date, duration, and so on). Accordingly, from a large amount of data, the user can find what he needs
There is also a "reset all filters" button that resets all filters and returns the full list of products.
However, with one of the filters, I ran into a problem: in this filter, I use a TextField from the mui. And the problem is that the values ​​themselves, by which the desired products are filtered, are discarded, and what the user entered in the field remains unchanged.
I will give an example: The user filters only by this field. Having received any result, he wants to return to the original list and presses the "reset all filters" button. All filters are reset and the original list is returned, but the data entered in the field remains, that is, the field itself is not cleared.
Help solve the problem
const MAX_DURATION = 9999999
export default function FilterDuration() {
const [minDuration, setMinDuration] = useState(0);
const [maxDuration, setMaxDuration] = useState(MAX_DURATION);
useEffect(() => {
updatedFilters.durationRange = { min: minDuration, max: maxDuration }
setFilters(updatedFilters)
if (maxDuration === 0) {
setMaxDuration(MAX_DURATION)
}
}, [minDuration, maxDuration])
return (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div style={{ width: "120px" }}>
<TextField
onInput={(e) => {
const newValue = Number(e.target.value)
if (newValue )
setMinDuration(newValue)
}} />
</div>
</div>
);
}

Add a value prop to your TextField which has the minDuration like this:
<TextField
type='number'
size="small"
margin="dense"
label="From"
value={minDuration}
onInput={(e) => {
e.target.value = Math.max(0, parseInt(e.target.value)).toString().slice(0,7)
const newValue = Number(e.target.value)
if (newValue <= maxDuration && newValue >= 0 && newValue <= MAX_DURATION)
setMinDuration(newValue)
}} />

You want the value of the TextField to be managed by the state of your component. Add a value attribute and set it to the current value of minDuration:
<TextField
...
value={minDuration}
/>

Related

how to validate user inputs in react using setState hook

I have a form. In the form I getting brandName, supplierName, and date of expiry from the user.
I am usinf TextField from mui library and submit button.
I want to disable submit button on empty form fields and enable it when user filled all the inputs
here is my code for declaring useStates
const[brandName, setBrandName] = useState("");
const[supplierName, setSupplierName] = useState("");
const[expiryDate, setExpiryDate] = useState(null);
const[brandNameError, setBrandNameError] = useState(false);
const[supplierNameError, setSupplierNameError] = useState(false);
const[expiryDateError, setExpiryDateError] = useState(false);
const[submitButton, setSubmitButton] = useState(true);
and here is all function which i used to validate my inputs
// checking brandName Error
const brandNameValidateOnBlur = ()=>{
if(brandName === ""){
setBrandNameError(true);
}
}
// checking supplier name error
const supplierNameValidateOnBlur = ()=>{
if(supplierName === ""){
setSupplierNameError(true);
}
}
// checking expiry date
const expiryDateValidateOnBlur = ()=>{
if(expiryDate === ""){
setExpiryDateError(true);
}
}
// now checking all inputs again if all inputs are good then
// button should be enabled
const checkAllInputs = ()=>{
if(brandName !== "" && supplierName !== "" && expiryDate !== ""){
setSubmitButton(false);
}else{
setSubmitButton(true);
}
}
and here is the rest of code
<TextField fullWidth id="productName" label="Product Name"
value={brandName.toLowerCase()} variant="outlined"
onChange={(data)=>{setBrandName(data.target.value.toUpperCase());checkAllInputs()}}
onBlur={brandNameValidateOnBlur}
onFocus={()=>setBrandNameError(false)}
error={brandNameError}
helperText = {brandNameError ? "Enter Brand Name" : ""}
/>
<TextField id="supplierName"
label="Supplier Name"
value={supplierName.toLowerCase()} variant="outlined"
onBlur={supplierNameValidateOnBlur}
onFocus={()=>setSupplierNameError(false)}
error={supplierNameError}
helperText={supplierNameError ? "Enter Supplier Name " : ""}
onChange={(data)=>{setSupplierName(data.target.value.toUpperCase());checkAllInputs()}} />
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DesktopDatePicker
label="Date Expiry"
inputFormat="MM/DD/YYYY"
value={expiryDate}
onBlur={expiryDateValidateOnBlur}
onFocus={()=>setExpiryDateError(false)}
error={expiryDateError}
helperText={expiryDateError ? "Enter Expiry Date ": ""}
onChange={(selectedDate)=>
{setExpiryDate(selectedDate.format("MM/DD/YYYY"));checkAllInputs()}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<Button type='button' variant='contained' id="submitButton" disabled={submitButton}
style={{backgroundColor:'orangered'}} onClick={addData}>Add Data</Button>
now problem is when i enter a single word my useState is updated but my checkAllInputs not working as i want to be like if i enter a word my checkAllInputs method runs before setting of state as i enter second entry then it works like I want to be so i did not know what i am doing wrong
I TRY useEffect hook like that
useEffect(()=>{
checkAllInputs();
},[brandName, supplierName, expiryDate]);
its working fine as i accepted but I read that it should be a expensive to use useEffect
and an other approach which i use
const checkAllInputsWithDom = ()=>{
let brandNameEntry = document.getElementById("brandName").value;
let supplierNameEntry = document.getElementById("supplierName").value;
let expiryDateEntry = document.getElementById("expiryDate").value;
if(brandNameEntry !=="" && supplierNameEntry !== "" && expiryDateEntry !== ""){
setSubmitButton(false);
}else{
setSubmitButton(true);
}
}
it is working out of box but I thing in react this is against react principles to direct manipulate
dome elements can I do it with out using useeffect hook like only with my metohd and one more thing
this did not set expiryDate error in date field
As long as you're keeping the input values in state, you don't need another variable storing the button state as well. You can calculate it at render time. (Read more about avoiding redundant state here.)
You could do something like this:
const Component = () => {
const[brandName, setBrandName] = useState("");
const[supplierName, setSupplierName] = useState("");
const[expiryDate, setExpiryDate] = useState("");
const disabled = brandName.length === 0 && supplierName.length === 0 && expiryDate.length === 0
return (
<>
<input value={brandName} onChange={(e) => setBrandName(e.target.value)}/>
<input value={supplierName} onChange={(e) => setSupplierName(e.target.value)}/>
<input value={expiryDate} onChange={(e) => setExpiryDate(e.target.value)}/>
<button disabled={disabled}>Button</button>
</>
)
}
here is the link for codesandbox I have a suggestion for you to refactor this 6 use States with just one useState as an object having all the values like the following:
const initialValues = {
brandName: "",
supplierName: "",
expiryDate: "",
brandNameError: "",
supplierNameError: "",
expiryDateError: ""
};
and then update it with a generic handleInputChange like below:
const handleInputChange = (e) => {
//const name = e.target.name
//const value = e.target.value
const { name, value } = e.target;
setValues({
...values,
[name]: value
});
let isEmpty = Object.values(values).some((x) => x === "");
console.log(isEmpty);
setIsDisabled(isEmpty);
};
FOR YOUR SOLUTION
You will have to just check all the values and just create a boolean with isDisabled so when all of those values are empty then it will only set to false.
I have created a code snippet for you, it needs some improvement but it will serve your purpose. Link already at the top and here as well
as suggested by Eduardo Motta de Moraes
I used this for making my button enabled after all inputs validate
const disabled= brandName.length === 0 || supplierName.length === 0 || expiryDate.length === 0;
this solve my problem
thanks for Eduardo Motta de Moraes for this

How to Handle Multiple Checkboxes in typescript

I am trying to handle multiple checkboxes as follows:
sendFileNameToBackEnd = (filename: string[]) => {
console.log(filename)
this.vizsualizaForFileName(filename)
}
render() {
const handleChange = (checked: boolean, event: React.FormEvent<HTMLInputElement>) => {
let listoffilename : Array<string> = []
const target = event.currentTarget;
const name = target.name;
if(checked && name !== '')
{
listoffilename.push(name)
this.setState({
listoffilename1: listoffilename
});
}
console.log(this.state.listoffilename1)
};
return (
<DataList aria-label="Checkbox and action data list example" isCompact >
{ this.state.fileListData ?
this.state.fileListData.map((fd) =>
<DataListItem aria-labelledby="check-action-item1">
<DataListItemRow>
<DataListCheck
id="controlled-check"
aria-labelledby="check-action-item"
isChecked={isChecked1}
onChange={handleChange}
name={fd}/>
<DataListItemCells
dataListCells={[
<DataListCell key="primary content">
<span id="check-action-item1">{fd ? fd : "Loading..."}</span>
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
):
<>
<EmptyState>
<Title headingLevel="h4" size="lg">Empty state</Title>
</EmptyState>
</>
}
</DataList>
<Button variant="primary" onClick={ () => {this.sendFileNameToBackEnd(this.state.listoffilename1)}}>Visualize</Button>
);
}
In the above code, when I select any checkbox, I can get its value, but when I choose multiple checkboxes I only getting one value in an array.
In 'this.state.fileListData' I have n number of data and I am displaying a checkbox using a map and one button called 'Visualize'. When we select some of the values from the checkbox and click on the 'Visualize' button, I want to send all selected values to the backend for another operation.
When I select multiple checkboxes then it only sends one value in it not all selected values.
Guide me if I am doing something wrong.

I need to subtract the count and update the remaining count

I am using TextField that field data I am adding into the table that works fine what my task is I have one field called Total no of count their I am storing my data count so whenever I add data into the table it will be based on the count, mean like example if I have count 3 then I am able to use count max 3 or if I want to divide that count with a different name that also works only max count I used whatever present in Total no of count or after dividing count with a user name I need to update remaining count in that field or whatever count is present after added into a table showing remaining count when I use all count in one time or add it to the table that works fine mean Total no of count get subtracted with table data count and remain 0 but when I divide that count into 2 or 3 names field mean by 1 by 1 then it will not work properly mean count not get subtracted properly
In this method, I am subtracting and setting the remaining count
const totalRemainingCount =
totalUsers -
Number(
AssignSearchesForm.values.countAssigned ||
teamdata?.map((data) => data.countAssigned)
);
export default function App() {
const [teamdata, setTeamData] = React.useState([]);
const AssignSearchesForm = useFormik({
initialValues: {
selectName: "",
selectAge: "",
location: "",
countAssigned: ""
},
validationSchema,
onSubmit: (values, formikHelper) => {
setTeamData([values, ...teamdata]);
formikHelper.resetForm();
}
});
let filteredArray = nameList.filter(
(e) => !teamdata.some((data) => data.selectName === e.selectName)
);
const handleChange = (e) => {
const selectedName = e.target.value;
const name = nameList.find((data) => data.selectName === selectedName);
const newOptions = Object.values(name).reduce((optionList, key) => {
optionList.push({ value: key, label: key });
return optionList;
}, []);
AssignSearchesForm.setFieldValue("selectName", selectedName);
AssignSearchesForm.setFieldValue("selectAge", newOptions[1]?.value || "");
AssignSearchesForm.setFieldValue("location", newOptions[2]?.value || "");
};
const totalUsers = 3;
const totalRemainingCount =
totalUsers -
Number(
AssignSearchesForm.values.countAssigned ||
teamdata?.map((data) => data.countAssigned)
);
return (
<div className="App">
<Card color="primary" variant="outlined">
<CardHeader
title={
<Typography variant="subtitle1">
Total no of count ={" "}
{totalRemainingCount <= 0 ? 0 : totalRemainingCount}
</Typography>
}
/>
<Divider />
<CardContent>
<Grid container direction="row" spacing={1}>
<Grid item xs={4}>
<TextField
sx={{ minWidth: 185 }}
select
id="outlined-basic"
label="Select Name"
name="selectName"
size="small"
onChange={handleChange}
value={AssignSearchesForm.values.selectName}
error={
AssignSearchesForm.errors.selectName &&
AssignSearchesForm.touched.selectName
}
helperText={
AssignSearchesForm.touched.selectName &&
AssignSearchesForm.errors.selectName
}
>
{filteredArray?.map((option) => (
<MenuItem key={option.selectName} value={option.selectName}>
{option.selectName}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={4}>
<TextField
id="outlined-basic"
label="location"
name="location"
size="small"
{...AssignSearchesForm.getFieldProps("location")}
error={
AssignSearchesForm.touched.location &&
AssignSearchesForm.errors.location
}
helperText={
AssignSearchesForm.touched.location &&
AssignSearchesForm.errors.location
}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="outlined-basic"
label="Select Age"
name="selectAge"
size="small"
{...AssignSearchesForm.getFieldProps("selectAge")}
error={
AssignSearchesForm.errors.selectAge &&
AssignSearchesForm.touched.selectAge
}
helperText={
AssignSearchesForm.touched.selectAge &&
AssignSearchesForm.errors.selectAge
}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="outlined-basic"
label="Count Assign"
name="countAssigned"
size="small"
type="number"
{...AssignSearchesForm.getFieldProps("countAssigned")}
error={
AssignSearchesForm.errors.countAssigned &&
AssignSearchesForm.touched.countAssigned
}
helperText={
AssignSearchesForm.touched.countAssigned &&
AssignSearchesForm.errors.countAssigned
}
/>
</Grid>
<Grid item xs={4}>
<Button
onClick={() => {
AssignSearchesForm.handleSubmit();
}}
variant="contained"
>
Add
</Button>
</Grid>
</Grid>
</CardContent>
</Card>
<Table teamdata={teamdata} />
</div>
);
}
CodeSandBox Link
You need to update your logic for the way you are calculating the count:
const totalRemainingCount =
totalUsers -
(parseInt(
AssignSearchesForm.values.countAssigned
? AssignSearchesForm.values.countAssigned
: 0,
10
) + teamdata?.reduce((partialSum, a) => partialSum + a.countAssigned, 0));
You were getting NaN because the data you were trying to use for subtraction was not the number. Here, I am doing the sum of countAssigned in the table and adding it with the form data that will allow you to get the right value.
Here is an example:https://codesandbox.io/s/preset-ranges-antd-4-19-2-forked-kczd1y?file=/App.js:1838-2095
What I have understood so far is that the Total count is not setting properly. If this is the case then you need to set state of count when you click add button, so that it stores the countAssigned value. Also using the max property in TextField to limit the count to remaining value.
I have edited your codesandbox example.

Formik instant feedback input box

I'm trying to make a input box component that has instant feedback using Formik. I want the input box to turn green when the user input matches a predefined string (the "answer"), gray if the input matches the prefix of the answer (including the empty string) and red otherwise. This string is stored as a property of the initial values, values.answer. The Formik validate function checks if the input equals values.answer and sets values.correct = true. I then created a css class corresponding to a green input box and set the className of the input conditional on the value of values.correct. The problem is it only seems to update (i.e turn green with a correct input) when I click out of focus of the input box (i.e onBlur). I would like it to work onChange. How would I do this?
Here is the relevant code sandbox: https://codesandbox.io/s/instant-feedback-box-lub0g?file=/src/Frame.js
Cool problem, but you've overcomplicated your code a little bit 😉 Some feedback:
touched is set to true during onBlur by default. You can override this by using setTouched(), but I found it simpler to just use values instead of touched in your form
try to keep values as minimal as possible, it's only meant to access input values so there's no need for hint and answer to be assigned to it
the purpose of the validation function is to return an errors object and not to set values, so remove assignments like values.correct = true
You don't need to store isDisabled in state, you can derive it from formik.submitCount and formik.isSubmitting
const Note = () => {
const [showFrame, setShowFrame] = useState({ 1: true });
const onCorrectSubmission = (frameId) => {
setShowFrame({ ...showFrame, [frameId]: true });
};
const text =
"What is the sum of the first three natural numbers? (give answer as a word, i.e one, two etc.)";
const hint = "The first three natural numbers are 1, 2, and 3";
const answer = "six";
return (
<div>
<h1>Induction</h1>
{showFrame[1] ? (
<Frame
id={1}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
{showFrame[2] ? (
<Frame
id={2}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
</div>
);
};
const Frame = ({
id,
text,
hint,
answer,
values,
onCorrectSubmission,
...props
}) => {
const validate = (values) => {
const errors = {};
if (!answer.startsWith(values.cloze)) {
errors.cloze = hint;
} else if (values.cloze !== answer) {
errors.cloze = true;
}
return errors;
};
const formik = useFormik({
initialValues: {
cloze: ""
},
validate,
onSubmit: (values) => {
onCorrectSubmission(id + 1);
}
});
const isFinished = formik.isSubmitting || formik.submitCount > 0;
return (
<form enablereinitialize={true} onSubmit={formik.handleSubmit}>
<p>{text}</p>
<input
id="cloze"
name="cloze"
type="text"
autoComplete="off"
{...formik.getFieldProps("cloze")}
disabled={isFinished}
className={`input
${!answer.startsWith(formik.values.cloze) ? "invalid-input" : ""}
${formik.values.cloze && !formik.errors.cloze ? "valid-input" : ""}
`}
/>
{formik.values.cloze && formik.errors.cloze ? (
<div>{formik.errors.cloze}</div>
) : null}
<button disabled={!!formik.errors.cloze || isFinished} type="submit">
Submit
</button>
</form>
);
};
export default Frame;
Live Demo

How can I print multiiple text areas when clicking on a button in reactjs

Within my reactjs class component, I want to create a button that opens a new text area everytime I click on it (e.g., when I click 5 times on it, it should open 5 textareas). In the current result, it only opens a textarea ones.
Thus, In a first step, I created a state with value 0 and create a function that should change the state:
// InitialState
state = {
value: 0,
};
onChange() {
this.setState({
value: this.state.value + 1,
});
}
In the next step, I rendered a button and created if-statements to show the textareas (which does not work):
render() {
return (
<div>
<IconButton onClick={this.onChange.bind(this)}>
<AddCircleOutlineIcon />
</IconButton>
<br />
{this.state.value >= 1 ? this.showTextArea() : null}
</div>
);
}
And this is my showTextArea function:
showTextArea = () => {
return (
<textarea
placeholder={this.state.placeholder}
value={this.state.value}
onChange={this.onChange1.bind(this)}
rows="2"
style={{ fontSize: "18px" }}
onFocus={(e) => (e.target.placeholder = "")}
onBlur={(e) => (e.target.placeholder = this.state.placeholder)}
/>
);
};
You condition is wrong. this.state.value >= 1 It should be like this because after first textbox opens and you click your button value will be 2 and first textbox will hide
This can be achieved using only single condition. Change your render method like this with for loop:
render() {
return (
<div>
<IconButton onClick={this.onChange.bind(this)}>
<AddCircleOutlineIcon />
</IconButton>
<br />
{
for (let i = 0; i < this.state.value; i++) {
{this.showTextArea()}
}
}
</div>
);
}

Categories