'How to get checked State in functional child component
I'm struggling with a simple problem in ReactJS. I'll have two functional components one parent component (list with elements of child) and one child component (listelement for parent)
Child Component (ListItem)
interface ListItemProps {
Name: string
SurName: string
Age: number
}
const ListItem: React.FC<ListItemProps> = (props) => {
const [checked, setChecked] = React.useState<boolean>(false);
const [effectiveAge, setEffectiveAge] = useState<number>(0);
function checkAge() {
if (props.Age=== effectiveAge) {
setChecked(true);
setIndeterminated(false);
}
else if (effectiveAge === 0) {
setChecked(false);
setIndeterminated(false);
}
else {
setIndeterminated(true);
setChecked(false);
}
}
const cardItem = (
<IonCard className="mx-0" color="white" disabled={props.Disabled}>
<IonItem className="nav-item pa-0" lines={"none"}>
<div className="h-100 ion-text-center mr-3 bg-yellow d-flex ion-justify-content-center ion-align-items-center" style={{ width: 83 }}>
<Checkbox sx={{ '& .MuiSvgIcon-root': { fontSize: 36 } }} color="default" slot="end" checked={checked} indeterminate={indeterminated} />
</div>
<IonCardContent>
<IonGrid>
<IonRow>
<IonCol size='5' class="ion-no-padding">
<IonLabel>Name</IonLabel>
</IonCol>
<IonCol size='7' class="ion-no-padding">
<IonLabel>{props.Name}</IonLabel>
</IonCol>
</IonRow>
<IonRow>
<IonCol size='5' class="ion-no-padding">
<IonLabel>Surname</IonLabel>
</IonCol>
<IonCol size='7' class="ion-no-padding">
<IonLabel>{props.SurName}</IonLabel>
</IonCol>
</IonRow>
<IonRow style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<IonCol size='5' class="ion-no-padding">
<IonLabel>Age</IonLabel>
</IonCol>
<IonCol size='7' class="ion-no-padding">
<IonInput type='number' value={effectiveAge} placeholder="0" onIonChange={e => { setEffectiveAge(e.detail.value === "0" || e.detail.value === null || e.detail.value === undefined || e.detail.value === "" ? 0 : parseInt(e.detail.value!)); checkAge() }} clearInput></IonInput>
</IonCol>
</IonRow>
</IonGrid>
</IonCardContent>
</IonItem>
</IonCard >
)
return cardItem;
}
export default ListItem;
Parent Component (where the List will be at the end)
interface IMember{
Name: string
SurName: string
Age: number
}
var Members: IMember[] = [
{
Name: Peter
SurName: Wright
Age: 22
},
{
Name: Stefan
SurName: Wright
Age: 25
},
{
Name: Petra
SurName: Wright
Age: 19
},
]
const MemberLines: React.FC = () => {
const [member, setMember] = useState<IMember[]>([]);
const [selectedItems, setSelectedItems] = useState<IMember[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const retrieveMember = () => Promise.resolve(Object.values(Member));
const resetForm = () => {
//reset the form with initial values
setMember(Member) //-> not working
}
const submitData = () => {
//Check if all ListItems are checked
}
const getMembers = React.useCallback(async () => {
try {
const result = await retrieveMember ();
setMember(result);
} catch (error) {
console.log(error);
}
}, []);
useEffect(() => {
getCommisioningLines();
}, []);
const navBarButtons: React.ReactElement[] = [];
return (
<Layout navBarButtons={navBarButtons} >
<IonRefresher slot="fixed" /* onIonRefresh={refreshMember} */>
<IonRefresherContent></IonRefresherContent>
</IonRefresher>
<PageHeading title={Member} icon={mdiPackageVariantClosed} />
<IonList className="bg-transparent" lines='none'>
<NavigationTitle title={Member} />
{member.map((value, key) => {
return (
<ListItem key={key} Name={value.Name} SurName={value.SurName!} Age={value.Age} />
)
})}
</IonList>
<div className="ion-text-right mt-2">
<IonButton onClick={resetForm} slot="end" type="button" color="danger">
<span className="mr-2">Reset</span>
</IonButton>
<IonButton onClick={submitData} slot="end" type="submit" fill="solid" color="primary">
<span className="mr-2">Submit</span>
</IonButton>
</div>
</Layout>
);
};
export default MemberLines;
My problem is: how can i reset all child functional components to the initial value (resetForm function) and how do i check if all ListItems are checked (i need it to check if i can submit the form). Anyone has an Idea?
Solution 1:[1]
1 - Reset form not working) To reset your form, you need to pass an array, not the interface, you can create a variable an assign that interface like:
const cleanMembers: Member = [];
setMember([]);
Or simply
setMember([]);
2 - Check if every child is selected) First things first... One smooth way to see that is using a property inside of the child object. You have now:
interface IMember{
Name: string
SurName: string
Age: number
}
So, I strongly recommend having a prop
selected
With this you can use a method from the Array called every. You could achieve what you want with:
member.every(item => item.selected)
In the case that you want to do at the hard way, you need to make sure that you have:
- A form wrapping all the component (including the childs)
- A handleChange that will need to be passed by prop from the parent where the form stands, because you don't access of the update state from the child if not handled from the parent. Remember, the component handle its own state
- Doing that you could achieve something like this:
const resetForm = () => setMember([]) const onSubmit = (values) => { const everyChildSelected = values.members.every(item => item.selected) } const handleChange = (e) => { // need to find which index is the member you're selecting // alter on the array members // and put the selected on or off } return ( // some code {member.length && member.map(m => ( ListItem data={m} handleChange={handleChange} )) })
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Matheus Alves |