I am creating the list from the skidList array and handling copy and delete operation on each skid as well as on click of delete button I am calling deleteSkid method which removes skid at that index and updates the list. However, Instead of deleting skid at a particular index, it deletes the last skid in the array list.
const CreateNewTab = () => {
const [skidList, setSkidList] = useState([]);
const [productNameMap, setproductNameMap] = useState({});
useEffect(() => {
let skidList = [];
let newSkid = {};
newSkid["2"] = "0";
newSkid["3"] = "0";
newSkid["4"] = "0";
skidList.push(newSkid);
setSkidList([...skidList]);
let productNameMap = {};
productNameMap["2"] = "PEN";
productNameMap["3"] = "PENCIL";
productNameMap["4"] = "ERASER";
setproductNameMap({ ...productNameMap });
}, []);
const updateProductQuantity = (skid, key, newQuantity, index) => {
console.log("Inside update Skid Quantity Index= " + index);
skid[key] = newQuantity;
let newSkidList = skidList;
newSkidList.splice(index, 1, skid);
console.log(newSkidList);
setSkidList([...newSkidList]);
};
const deleteSkid = (index) => {
console.log(index);
let newSkidList = skidList;
newSkidList.splice(index, 1);
console.log("Skid deleted from: " + newSkidList);
setSkidList([...newSkidList]);
};
const insertSkid = (skid) => {
setSkidList(skidList.concat([{ ...skid }]));
};
return (
<div>
{skidList.flatMap((skid, index) => (
<div style={{ marginRight: "0", paddingRight: "0" }}>
<Row style={{ margin: "0", padding: "0" }}>
<Col span={19}>
{Object.keys(skid).map((key) => (
<Row>
<Col span={16}>
<h6>{productNameMap[key.toString()]}</h6>
</Col>
<Col span={8}>
<InputNumber
min={0}
defaultValue={skid[key]}
rules={[
{
required: true,
message: "Please input quantity!",
},
]}
onChange={(newQuantity) => {
updateProductQuantity(skid, key, newQuantity, index);
}}
/>
</Col>
</Row>
))}
</Col>
<Col span={5}>
<Row>
<Col span={12}>
<Button
type="primary"
icon={<CopyOutlined />}
size="large"
shape="circle"
onClick={() => insertSkid(skid)}
/>
</Col>
<Col spna={12}>
<Button
type="primary"
size="large"
shape="circle"
danger
icon={<DeleteOutlined />}
onClick={() => deleteSkid(index)}
/>
</Col>
</Row>
}
</Col>
</Row>
<Divider />
</div>
))}
</div>
);
};
It seems it has something to do in how react recycles the dom or something, the item does get deleted from the array but somehow the value is kept in the dom because you're using defaultValue instead value in your inputs, make this little change and it will work
value={skid[key]}
https://codesandbox.io/s/compassionate-ride-1yoti?file=/src/App.js
Your implementation of deleteSkid() is fine,
But you pass the value to the InputNumber as defaultValue so it won't change when the component re-render.
Just replace it with value and it should work.
<InputNumber
min={0}
value={skid[key]}
rules={[
{
required: true,
message: "Please input quantity!"
}
]}
onChange={(newQuantity) => {
updateProductQuantity(skid, key, newQuantity, index);
}}
/>
Related
I have the component below where I'm trying the build functionality to allow a user to update opening times of a store.
I'm passing the original opening times as a prop and creating some state using the props opening times for initial state. I want to use the new state to submit changes but if the user selects cancel the UI updates to reflect the original times.
I have most of the functionality working but for some reason my handler to update the state with the input change also seems to update the props value so it won't go back to the original value.
How can I stop the props updating and ensure only the allOpeningHours state is changed?
VIDEO: https://www.veed.io/view/c40bf9e8-7502-408a-ba6d-fd306dbf4b6f?sharingWidget=true
const EditStudioHours: FC<{ studio: Studio }> = ({ studio }) => {
const { value: edit, toggle: toggleEdit } = useBoolean(false)
const { value: submitting, toggle: toggleSubmitting } = useBoolean(false)
const [allOpeningHours, setAllOpeningHours] = useState([
...studio.openingHours.regularDays,
])
return (
<Box>
<Typography variant='h6' mt={2} gutterBottom>
Set standard hours
</Typography>
<Typography fontWeight='light' fontSize={14}>
Configure the standard operating hours of this studio
</Typography>
<Stack mt={3} spacing={2}>
{studio.openingHours.regularDays.map((hours, i) => (
<DayOfWeek
dow={daysOfWeek[i]}
openingHours={hours}
edit={edit}
i={i}
setAllOpeningHours={setAllOpeningHours}
allOpeningHours={allOpeningHours}
/>
))}
</Stack>
<Button
variant={edit ? 'contained' : 'outlined'}
onClick={() => {
toggleEdit()
}}
fullWidth
sx={{ mt: 2 }}
disabled={!edit ? false : submitting}
>
{submitting ? (
<CircularProgress size={22} />
) : edit ? (
'Submit changes'
) : (
'Edit'
)}
</Button>
{edit && (
<Button
onClick={toggleEdit}
variant={'outlined'}
sx={{ mt: 1 }}
fullWidth
>
Cancel
</Button>
)}
</Box>
)
}
export default EditStudioHours
const DayOfWeek: FC<{
openingHours: { start: number; end: number }
dow: string
edit: boolean
i: number
setAllOpeningHours: any
allOpeningHours: any
}> = ({ openingHours, dow, edit, i, setAllOpeningHours, allOpeningHours }) => {
const [open, setOpen] = useState(openingHours.end !== openingHours.start)
const handleOpenClose = () => {
open &&
setAllOpeningHours((ps: any) => {
const newHours = [...ps]
newHours[i].start = 0
newHours[i].end = 0
return newHours
})
setOpen((ps) => !ps)
}
const handleStart = (e: any) => {
setAllOpeningHours((prevState: any) => {
const newHours = [...prevState]
newHours[i].start = e.target.value
return newHours
})
}
const handleEnd = (e: any) => {
setAllOpeningHours((ps: any) => {
const newHours = [...ps]
newHours[i].end = e.target.value
return newHours
})
}
return (
<Box display='flex' alignItems='center' justifyContent={'space-between'}>
<Box display={'flex'} alignItems='center'>
<Typography width={150}>{dow}</Typography>
<FormGroup>
<FormControlLabel
control={
<Switch
disabled={!edit}
checked={open}
onChange={handleOpenClose}
/>
}
label='Open'
/>
</FormGroup>
</Box>
{open && (
<Box display={'flex'} alignItems='center'>
<TextField
disabled={!edit}
id={`${i}open`}
select
label='Open'
value={edit ? allOpeningHours[i].start : openingHours.start}
type='number'
sx={{ minWidth: 120 }}
size='small'
onChange={handleStart}
>
{openingOptions.map((option: { value: number; label: string }) => (
<MenuItem dense key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
<Typography mx={2}>TO</Typography>
<TextField
disabled={!edit}
id={`${i}close`}
select
label='Close'
value={edit ? allOpeningHours[i].end : openingHours.end}
type='number'
sx={{ minWidth: 120 }}
size='small'
onChange={handleEnd}
>
{openingOptions.map((option: { value: number; label: string }) => (
<MenuItem dense key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
</Box>
)}
</Box>
)
}
The problem is that objects within the studio.openingHours.regularDays array still share the same reference, even though you copied the array itself.
When you use something like
newHours[i].start = e.target.value
You're still updating the original objects from props.
You can use Array.prototype.splice() to remove the object at index i and replace it with a new one
const day = newHours[i];
newHours.splice(i, 1, {
...day,
start: e.target.value,
});
Do this in each of your 3 handle functions.
Alternately, break all references when creating local state from props
const [allOpeningHours, setAllOpeningHours] = useState(
studio.openingHours.regularDays.map((day) => ({ ...day }))
);
I made such a grid using grid antd . code here.
const { Row, Col } = antd;
const App = () => (
<Row>
<Col className={'first'} span={16}>
<img src="https://picsum.photos/800/400?random=1"/>
</Col>
<Col span={8}>
<Row>
<Col className={'second'} span={24}>
<img src="https://picsum.photos/800/400?random=2"/>
</Col>
<Col className={'third'} span={24}>
<img src="https://picsum.photos/800/400?random=3"/>
</Col>
</Row>
</Col>
</Row>
)
const ComponentDemo = App;
ReactDOM.render(<ComponentDemo />, mountNode);
I'm getting data from the server. There may be more than 3 of them there. I should output the first 3 like this. The rest will be displayed after pressing the button. How can this effect be achieved in myData.map(). To output these elements without using indexes?
I will try do somthing like this
dataSale.slice(0,maxCount).map(({...item},index)=>(
(index===0)?(
<Col key = {index} span={16}>
<SaleCard {...item}/>
</Col>
):(
<Col key={index} span={8}>
<SaleCard {...item}/>
</Col>
)
))
I hope this helps. The following code should work with any number of image links sent by the server. I have mocked with 9 images.
I followed components approach, like what you are supposed to when working with React. I created some components and pieced them back-together to create the design that you wanted to with map().
const { Row, Col, Button } = antd;
const {useState} = React;
const data = [
"https://picsum.photos/800/400?random=1",
"https://picsum.photos/800/400?random=2",
"https://picsum.photos/800/400?random=3",
"https://picsum.photos/800/400?random=4",
"https://picsum.photos/800/400?random=5",
"https://picsum.photos/800/400?random=6",
"https://picsum.photos/800/400?random=7",
"https://picsum.photos/800/400?random=8",
"https://picsum.photos/800/400?random=9",
]
const ColWithImage = (props) => (
<Col span={props.n % 3 == 1 ? 16 : 24}>
<img src={data[props.n-1]}/>
</Col>
)
const MainRow = (props) => {
const k = props.n*3 + 1;
return (
<Row>
<ColWithImage n={k}/>
<Col span={8}>
<Row>
<ColWithImage n={k+1}/>
<ColWithImage n={k+2}/>
</Row>
</Col>
</Row>
)}
const Container = (props) => {
// Create an iterable array depending upon the number of image links
const arr = Array.from(Array(Math.floor(data.length/3)))
// Show only one row if the button is not clicked
// But show all the rows if the button is clicked
return !props.buttonClicked
? <><MainRow n={0}/></>
: (<>
{
arr.map((item, index) => <MainRow key={index} n={index}/>)
}
</>)
}
const App = () => {
const [buttonClicked, setButtonClicked] = useState(false);
const [buttonText, setButtonText ] = useState("Show More");
const handleClick = () => {
setButtonClicked(!buttonClicked);
setButtonText(buttonClicked ? "Show More" : "Show Less");
}
return (<>
<Container buttonClicked={buttonClicked}/>
<Button onClick={handleClick}>{buttonText}</Button>
</>)
}
const ComponentDemo = App;
ReactDOM.render(<ComponentDemo />, mountNode);
You can view the result here.
I created a SalesView component and it will render the layout as per you requirement. It receives an items array of length less than or equal to 3 (Suppose you have total 5 records, only two records will be displayed in second row).
Hope this solution solves your problem.
import { useState } from "react";
import { Row, Col, Button } from "antd";
import "antd/dist/antd.min.css";
const list = Array.from({ length: 20 }).map((_, i) => ({
id: i,
url: `https://picsum.photos/800/400?random=${i + 1}`,
}));
const SalesCard = ({ id, url }) => {
return <img src={url} />;
};
const SalesView = ({ items }) => {
return (
<Row>
{items?.[0] && (
<Col span={16}>
<SalesCard {...items[0]} />
</Col>
)}
{items.length > 2 && (
<Col span={8}>
<Row>
{items?.[1] && (
<Col span={24}>
<SalesCard {...items[1]} />
</Col>
)}
{items?.[2] && (
<Col span={24}>
<SalesCard {...items[2]} />
</Col>
)}
</Row>
</Col>
)}
</Row>
);
};
function App() {
const [showAll, setShowAll] = useState(false);
const totalChunks = Math.ceil(list.length / 3);
const data = Array.from({ length: showAll ? totalChunks : 1 }).map((_, index) => {
const startIndex = index * 3;
const endIndex = startIndex + 3;
return <SalesView key={index} items={list.slice(startIndex, endIndex)} />;
});
const onClick = () => setShowAll(true);
return (
<>
{data}
{!showAll && <Button onClick={onClick}>Show More</Button>}
</>
);
}
export default App;
you can achieve it via CSS or use ternary
dataSale.map((item, index) =>
<Col key={index} span={index < 3 ? 16 : 8}>
<SaleCard {...item}/>
</Col>
)
I want to set all TextField to the initial state after adding data to the table by clicking ADD button
I am able to set that to the initial state by using AssignSearchesForm.resetForm(); but what happens here if I set this in add button click it will reset data but my table data also get removed because it's updated Formik values setTeamData([values, ...teamdata]);
I am adding Formik value in this state after onClick it will get added but if I reset form and set initial this state value also getting set empty so what I want after adding data into table TextField get reset or initial but table data will stay the same either if we do not update that
This way I am trying
<Button
onClick={() => {
AssignSearchesForm.handleSubmit();
// I tried this two way
//first way
// AssignSearchesForm.resetForm();
//second way
// AssignSearchesForm.setFieldValue("selectName","")
// AssignSearchesForm.setFieldValue("selectAge","")
// AssignSearchesForm.setFieldValue("location","")
}}
variant="contained"
>
Add
</Button>
export default function App() {
const [teamdata, setTeamData] = React.useState([]);
const AssignSearchesForm = useFormik({
initialValues: {
selectName: "",
selectAge: "",
location: ""
},
validationSchema,
onSubmit: (values) => {
setTeamData([values, ...teamdata]);
}
});
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 || "");
};
return (
<div className="App">
<Grid container direction="row" spacing={1}>
<Grid item xs={4}>
<TextField
sx={{ minWidth: 150 }}
select
id="outlined-basic"
label="Select Name"
name="selectName"
size="small"
onChange={handleChange}
value={AssignSearchesForm.values.selectName}
error={
AssignSearchesForm.errors.selectName &&
AssignSearchesForm.touched.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")}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="outlined-basic"
label="Select Age"
name="selectAge"
size="small"
{...AssignSearchesForm.getFieldProps("selectAge")}
/>
</Grid>
<Grid item xs={4}>
<Button
onClick={() => {
AssignSearchesForm.handleSubmit();
// AssignSearchesForm.resetForm();
// AssignSearchesForm.setFieldValue("selectName","")
// AssignSearchesForm.setFieldValue("selectAge","")
// AssignSearchesForm.setFieldValue("location","")
}}
variant="contained"
>
Add
</Button>
</Grid>
</Grid>
<Table teamdata={teamdata} />
</div>
);
}
You can leverage the second parameter formikHelper in onSubmit function of formik.
Better approach pointed out by dev-developer in comments:
onSubmit: (values, formikHelper) => {
setTeamData([values, ...teamdata]);
formikHelper.resetForm();
}
Another alternative approach if resetForm() is not useful.
onSubmit: (values, formikHelper) => {
setTeamData([values, ...teamdata]);
formikHelper.setFieldValue("selectName", "");
formikHelper.setFieldValue("selectAge", "");
formikHelper.setFieldValue("location", "");
}
Here is the modified code sandbox
I'm trying to bind to a nested array to text inputs from a form. There are 6 blocks and each block contains 3 values. I have initially populated the array for mapping by using:
new Array(6).fill(['','',''])
and rendering the values using 2 loops. One for the 6 blocks and the other 3 values inside each block. The 3 values are the ones binding to the form. I refer to each of the inputs using the second parameter of the map function which is the index.
Here is the code in its entirety.
import {
Card,
CardContent,
CardHeader,
CardMedia,
TextField,
Grid,
Typography,
Button,
} from "#material-ui/core";
import axios from "axios";
import { useState } from "react";
import { useHistory } from "react-router";
export default function CreateEditQuestion() {
const answerPack = new Array(6).fill(["", "", ""]);
const [question, setQuestion] = useState("");
const [answers, setAnswer] = useState(answerPack);
const [TimeAllowed, setTimeAllowed] = useState(30);
const [Score, setScore] = useState(1);
const [Date, setDate] = useState("");
const history = useHistory();
const handleAnswerSet = (value, answerIndex, answerTextIndex) => {
var updatedAnswer = answers;
updatedAnswer[answerIndex][answerTextIndex] = value;
setAnswer(updatedAnswer);
};
const handleSubmit = () => {
let data = {
question,
answer_set: answers,
time_allowed: TimeAllowed,
score_value: Score,
date: Date,
};
for (const [key, value] of Object.entries({ question, Date })) {
if (value.trim().length == 0) {
alert(`${key} has not been filled in`);
return false;
}
}
axios
.post(
"https://localhost:8000/question",
data
)
.then((resp) => {
alert("Succesfully added Question");
history.push("/question");
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<h1>Create Question</h1>
<Card elevation={1}>
<CardHeader></CardHeader>
<CardContent>
<div>
<TextField
fullWidth
label="Question"
variant="outlined"
onChange={(e) => {
setQuestion(e.target.value);
}}
></TextField>
</div>
<Grid container direction={"row"} spacing={4}>
{answers.map((answerTexts, i) => {
return (
<Grid key={i} item md={4} width={50}>
{answerTexts.map((text, j) => {
return (
<div style={{ width: "70%", marginTop: "30px" }}>
<TextField
fullWidth
label={`Answer ${i + 1} - ${j + 1}`}
onChange={(ev) => {
handleAnswerSet(ev.target.value, i, j);
}}
variant="outlined"
/>
<br />
</div>
);
})}
</Grid>
);
})}
</Grid>
<Grid container direction={"row"} spacing={4}>
<Grid item md={5} width={80}>
<Typography variant="h6" gutterBottom gutterTop>
Extra Options
</Typography>
<TextField
label={"Time Allowed : "}
variant="outlined"
defaultValue={TimeAllowed}
onChange={(ev) => {
setTimeAllowed(ev.target.value);
}}
/>{" "}
<TextField
label="Score"
variant="outlined"
defaultValue={Score}
onChange={(ev) => {
setScore(ev.target.value);
}}
/>
</Grid>
<Grid item md={5} width={100}>
<Typography variant="h6" gutterBottom gutterTop>
Question Date
</Typography>
<TextField
type="date"
onChange={(ev) => {
setDate(ev.target.value);
}}
/>
</Grid>
</Grid>
<div align="center">
<Button
onClick={() => {
handleSubmit();
}}
variant="contained"
color="primary"
>
Submit Question
</Button>
</div>
</CardContent>
</Card>
</>
);
}
Problem:
On each block, changing any value also changes all the other corresponding inputs in other blocks, so if the first input on the first block is changed, then all other first inputs in the other blocks also get changed,e.g(changing answer 1-1 also changes 2-1,3-1,4-1, etc). I could not trace why. Only the corresponding values should be changed
This is the function responsible for setting the values.
const handleAnswerSet = (value, answerIndex, answerTextIndex) => {
var updatedAnswer = answers;
updatedAnswer[answerIndex][answerTextIndex] = value;
setAnswer(updatedAnswer);
};
I am using react Ref in custom input field but it is not getting input content.
I am creating some ref in React class component and using them in form custom input field refs. then i have a button in the form which has "onClick" event to get that data. I am always getting undefined in console when i am getting "ref.current.value"
class LandingAfterSignIn extends React.Component {
titleTextFieldRef = React.createRef();
shortDescriptionMetaTextFieldRef = React.createRef();
longDescriptionMetaTextFieldRef = React.createRef();
imageFieldRef = React.
createRef();
webFieldRef = React.createRef();
contactTextFieldRef = React.createRef();
constructor(props) {
super(props);
// we use this to make the card to appear after the page has been rendered
this.state = {
cardAnimaton: "cardHidden",
isLoading: false,
error: null
};
// super(props);
this.state = {
errors: []
};
}
render() {
let image= require("assets/img/landing-bg.jpg") ;
const { classes, ...rest } = this.props;
//let titleTextFieldRef= this.titleTextFieldRef ;
//let shortDescriptionMetaTextFieldRef = this.shortDescriptionMetaTextFieldRef ;
return (
<div
className={classes.pageHeader}
style={{
backgroundImage: "url(" + image + ")",
backgroundSize: "cover",
backgroundPosition: "top center",
width: "100%"
}}
>
<Header
color="transparent"
routes={dashboardRoutes}
brand="StockNap"
rightLinks={<HeaderLinks />}
fixed
changeColorOnScroll={{
height: 400,
color: "white"
}}
{...rest}
/>
<div>
<div className={classes.container}>
<div className={classes.container}>
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<h1 className={classes.title}>Welcome {firebase.auth().currentUser.displayName} </h1>
<FirebaseDatabaseMutation type="push" path="user_bookmarks">
{({ runMutation }) => (
<form>
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="Company Name/Title"
id="titleTextField"
formControlProps={{
fullWidth: true
}}
inputRef={this.titleTextFieldRef}
>
</CustomInput>
</GridItem>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="short Description"
id="shortDescription"
formControlProps={{
fullWidth: true,
className: classes.textArea
}}
inputRef={this.shortDescriptionMetaTextFieldRef}
/>
</GridItem>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="long Description"
id="longDescription"
formControlProps={{
fullWidth: true,
className: classes.textArea
}}
inputProps={{
multiline: true,
rows: 2
}}
inputRef={this.longDescriptionMetaTextFieldRef}
/>
</GridItem>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="contact"
id="contactTextField"
formControlProps={{
fullWidth: true,
}}
inputRef={this.contactTextFieldRef}
/>
</GridItem>
<Button
style={{
width: 50,
height: 50,
alignSelf: "center",
background: "#039BE5",
color: "white"
}}
variant="fab"
type="submit"
onClick={async ev => {
console.log("submit") ;
ev.preventDefault();
ev.stopPropagation();
ev.nativeEvent.stopImmediatePropagation();
const titleTextField = get(
this.titleTextFieldRef,
"current.value",
""
);
const shortDescriptionMetaTextField = get(
this.shortDescriptionMetaTextFieldRef,
"current.value",
""
);
const longDescriptionkMetaTextField = get(
this.longDescriptionMetaTextFieldRef,
"current.value",
""
);
const contactTextField = get(
this.contactTextFieldRef,
"current.value",
""
);
console.log(this.titleTextFieldRef);
console.log(this.shortDescriptionMetaTextFieldRef);
await runMutation({
titleTextField: titleTextField,
shortDescriptionMetaTextField: shortDescriptionMetaTextField,
created_at: firebase.database.ServerValue.TIMESTAMP,
updated_at: firebase.database.ServerValue.TIMESTAMP
});
set(this.titleTextFieldRef, "current.value", "");
set(this.shortDescriptionMetaTextFieldRef, "current.value", "");
}}
>
+
</Button>
</GridContainer>
</form>
)}
</FirebaseDatabaseMutation>
</GridItem>
</GridContainer>
</div>
</div>
</div>
<div className={classNames(classes.main, classes.mainRaised)}>
<div className={classes.container}>
</div>
</div>
<br/>
<Footer />
</div>
);
}
}
I want to add one more information , customInput are functional components
i want reference from parents to go into that
function CustomInput({ ...props }) {
const {
classes,
formControlProps,
labelText,
id,
labelProps,
inputProps,
error,
white,
inputRootCustomClasses,
success
} = props;
const labelClasses = classNames({
[" " + classes.labelRootError]: error,
[" " + classes.labelRootSuccess]: success && !error
});
const underlineClasses = classNames({
[classes.underlineError]: error,
[classes.underlineSuccess]: success && !error,
[classes.underline]: true,
[classes.whiteUnderline]: white
});
const marginTop = classNames({
[inputRootCustomClasses]: inputRootCustomClasses !== undefined
});
const inputClasses = classNames({
[classes.input]: true,
[classes.whiteInput]: white
});
var formControlClasses;
if (formControlProps !== undefined) {
formControlClasses = classNames(
formControlProps.className,
classes.formControl
);
} else {
formControlClasses = classes.formControl;
}
return (
<FormControl {...formControlProps} className={formControlClasses}>
{labelText !== undefined ? (
<InputLabel
className={classes.labelRoot + " " + labelClasses}
htmlFor={id}
{...labelProps}
>
{labelText}
</InputLabel>
) : null}
<Input
classes={{
input: inputClasses,
root: marginTop,
disabled: classes.disabled,
underline: underlineClasses
}}
id={id}
{...inputProps}
/>
</FormControl>
);
}
From the official docs:
In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props.
Your first inclination may be to use refs to “make things happen” in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to “own” that state is at a higher level in the hierarchy. See the Lifting State Up guide for examples of this.
Currently, you're exactly doing what you should not be doing: using refs to access component methods. That is anti-pattern and make your code suceptible to bugs like you're experiencing right now. You should either:
Lift the state up if you need to pass data from one component to another
Use state container libraries like Redux if you need to trigger actions
In either way I strongly advise you to refactor your code in either direction.