Mat ui FormControlLabel not using correct state - javascript

I am using FormControlLabel for my custom checkboxes but they don't seem to be checked/unchecked based on the state being passed to them.
The onchange handler updates everything in redux/BE but on initial render or after reload they are all unchecked again.
I have tried so many methods that I have lost track and ended up in a bit of a spiral.
I have also tried controlling the checkbox using both value and checked.
when the checkboxes are rendered via the .map they have the correct boolean value being received from the state but the checkbox doesn't reflect that.
Any ideas where I might be going wrong?
I've put the file in a gist here: https://gist.github.com/SerdarMustafa1/052bffd6958858eb5770991f59d1e187
styled checkbox is :
export const CustomCheckbox = withStyles({
root: {
color: `${COLORS.MAIN_ORANGE}`,
"&$checked": {
color: `${COLORS.MAIN_ORANGE}`
}
},
checked: {}
})(props => <Checkbox color="default" {...props} />);
and the redux action/reducer:
export const SET_IS_PIN_CODE_CHECK_IN_CHECKED =
"venueTemplates/SET_IS_PIN_CODE_CHECK_IN_CHECKED";
export const setIsPinCodeCheckInChecked = createAction(
SET_IS_PIN_CODE_CHECK_IN_CHECKED
);
export const handlers = {
[SET_IS_PIN_CODE_CHECK_IN_CHECKED]: (state, { payload: value }) => ({
...state,
isPinCodeCheckInChecked: value
}),
Been going around in circles for ~10hrs, any ideas or help appreciated.

Maybe not the best solution but what worked for me was to use defaultChecked
example:
defaultChecked={item?.isPinCodeCheckInChecked}

You need to use checked (boolean) instead of value (string):
<CustomCheckbox
id={id || ""}
onChange={event => handlePinCodeChecked(event)}
checked={!!isPinCodeCheckInChecked[id]}
name={name || ""}
/>

Related

Set state for dynamically generated component in React

I'm reusing a couple of external components to create my custom Combobox in strapi app.
Values are received from server so I need to add options dynamically.
Currently there is the following code:
import React, { useState, useEffect } from "react";
import {
Combobox,
ComboboxOption
} from "#strapi/design-system";
export default function ComboboxCustom({
valuesList,
valueSelected
}) {
const [value, setValue] = useState('');
const combo = (<Combobox label="Country" value={value} onChange={setValue}>
{valuesList.map((entry) => {
return(
<ComboboxOption value="{entry.id}">{entry.name}</ComboboxOption>
);
})}
</Combobox>);
// setValue(valueSelected)
return combo;
}
And everything goes good until I try so set 'selected' option basing on another set of data. In static world I could just say useState(valueSelected) and it will work. But as code generated dynamically, there is no related option yet, so I get failure like "Failed to get 'props' property of undefined".
I tried to put this combobox into a variable and set state between creation and returning it (commented setValue line before the return statement) but then app gets in a loop and returns "Too many re-renders".
Does anyone has an idea of how to change/rewrite this to be able to set selected value for dynamically created combobox?
So I assume that the values are dynamically fetched and passed to the ComboboxCustom.
I think you can add setValue(valueSelected) inside an useEffect.
onChange of the prop valueSelected.something like,
useEffect(() => {
setValue(valueSelected)
}, [valueSelected])
Also handle the return when the value is not yet loaded. like before doing valuesList.map, first check if valueList ? (render actual) : (render empty)
Hope this helps!!
Thanks,
Anu
Finally I got working solution based on answer from #Anu.
Cause valuesList is got as GET-request from another hook, I have to check values are already present (first hook hit gives [] yet) and bind Combobox state updating to change of valuesList also. Though I don't fell like this solution is perfect.
import React, { useState, useEffect } from "react";
import {
Combobox,
ComboboxOption
} from "#strapi/design-system";
export default function ComboboxCustom({
valuesList,
valueSelected,
}) {
const [value, setValue] = useState('');
let combo = null;
useEffect(() => {
if(combo && combo?.props?.children?.length > 0 && valuesList.length > 0) {
setValue(valueSelected)
}
}, [valueSelected, valuesList])
combo = (<Combobox label="Country" value={value?.toString()} onChange={setValue}>
{valuesList.map((entry) => {
return(
<ComboboxOption value={entry?.id?.toString()}>{entry.name}</ComboboxOption>
);
})}
</Combobox>);
return combo;
}
After that I decided avoid creating custom component based on already published as I'll need to add and process event listeners that are added for us in the existing components. So I placed this code directly into my modal and it also works:
const [countries, setCountries] = useState([]);
const [deliveryCountryValue, setDeliveryCountryValue] = useState('');
useEffect(async () => {
const countriesReceived = await countryRequests.getAllCountries();
setCountries(countriesReceived);
}, []);
useEffect(() => {
// If there is no selected value yet, set the one we get from order from server
const valueDelivery = deliveryCountryValue != '' ? deliveryCountryValue : order.country?.id;
if(countries.length > 0) {
setDeliveryCountryValue(valueDelivery);
order.country = countries.find(x => x.id == valueDelivery);
}
}, [deliveryCountryValue, countries])
<Combobox key='delivery-combo' label="Country" value={deliveryCountryValue?.toString()} onChange={setDeliveryCountryValue}>
{countries.map((entry) => {
return(
<ComboboxOption key={'delivery'+entry.id} value={entry?.id?.toString()}>{entry.name}</ComboboxOption>
);
})}
</Combobox>

How to use setState along with dispatch from redux - it re-renders whole component and restarts updated setState

I have this component
const CheckboxColumn: FC<ICheckboxColumnProps> = ({ id_llamada }) => {
// select pickup
const dispatch = useTypedDispatch();
const [isPickupSelected, setIsPickupSelected] = useState(false);
const selectPickup = (e: ChangeEvent<HTMLInputElement>) => {
setIsPickupSelected(e.target.checked);
dispatch(controlPickup(id_llamada));
};
return (
<TableCell
component={'div'}
padding="checkbox"
sx={{ padding: '0px 0px 0px 4px' }}
onClick={(e) => {
e.stopPropagation();
}}
>
<Checkbox
color="primary"
checked={isPickupSelected ? true : false}
// disabled={true}
sx={{
padding: '7px',
'&:hover': {
backgroundColor: 'rgba(0, 0, 0, 0.06)',
},
}}
onChange={selectPickup}
/>
</TableCell>
);
};
export default CheckboxColumn;
And I need to be able to select a table's row, but also dispatch its information to redux, the problem, is that, I can't figure out how to use setState along with dispatch.
What is currently happening is that my isPickupSelected is not updating its value, but data it's actually being saved in my reducer, and, when I comment the dispatch(function()) my state is being able to work properly.
I've been trying to search solutions to this, and one of them was that, I should use useEffect and whenever my state changes, I should dispatch the function, and it works, but immediately, it literally restarts my component, and also my isPickupSelected so my state is no longer updated but returns to its original value of false.
What am I supposed to do in this case?
You shouldn't have to make the equality check below since isPickupSelected is already a boolean value.
checked={isPickupSelected ? true : false}
Try simply using:
<Checkbox
color="primary"
checked={isPickupSelected}
...
Also, given that the checkbox can only be checked or unchecked, you actually could do:
const selectPickup = () => {
setIsPickupSelected(!isPickupSelected) // instead of `e.target.checked`
This way, when the user clicks on the checkbox, the state setter will set isPickupValue to the opposite of whatever the current value is (toggling).
Let me know if that fixes your issues? Cheers
The difference between how your dispatch is updating the state vs your setIsPickupSelected, is your dispatched state is relying on the previous state value to determine the next, where as your setter is relying on the UI elements value. That UI elements value is being controlled by the isPickupState, and so when the checkbox is clicked, the value passed in will be the value you get from e.target.checked. To fix this, you will need to control that state separately from the UI element. You are already doing this with the isPickupSelected state variable.
Also, since your checkbox component is the only one consuming a state, you may be able to save your TableCell a rerender each time that checkbox changes. All you have to do is hoist that logic into a separate component.
const ControlledCheckbox : FC<IControlledCheckboxProps> = () => {
const dispatch = useTypedDispatch();
const [isPickupSelected, setIsPickupSelected] = useState(false);
const selectPickup = () => {
setIsPickupSelected(!isPickupSelected);
dispatch(controlPickup(id_llamada));
};
return <Checkbox
color="primary"
checked={isPickupSelected ? true : false}
// disabled={true}
sx={{
padding: '7px',
'&:hover': {
backgroundColor: 'rgba(0, 0, 0, 0.06)',
},
}}
onChange={selectPickup}
/>
}
const CheckboxColumn: FC<ICheckboxColumnProps> = ({ id_llamada }) => {
return (
<TableCell
component={'div'}
padding="checkbox"
sx={{ padding: '0px 0px 0px 4px' }}
onClick={(e) => {
e.stopPropagation();
}}
>
<ControlledCheckbox />
</TableCell>
);
};
export default CheckboxColumn;
Solution 1: Remove checked prop from Checkbox, leave it uncontrolled input.
// checked={isPickupSelected ? true : false}
Solution 2: In handler func:
setIsPickupSelected(prevState => !prevState)
Is prop id_llamada coming from the Redux store?
If that's the case, then the component will re-render thus setting isPickedUpSelected back to false. Why? I assume the dispatch updates the value of id_llamada on a parent level causing the rendering process to catch the update and re-render the component as it gets passed back down to the component.
The above would be the root cause of why the component state is not updated and that's why when you comment out the dispatch the state update works.
To avoid this, you may want to use the store value for id_llamada instead of passing it as a prop. Perhaps you want to read about Redux Selectors.
Also, as an observation & unrelated to the initial problem, since isPickupSelected is a boolean you can simplify checked={isPickupSelected ? true : false} to checked={isPickupSelected}.

I need to set the checkbox to enabled when its value is true

I need to set like if the value of check box is coming true then check box show selected if the value comes false check box gets selected but what happening its value getting true from the backend I am storing value in the state rules but it's not getting enable and if the value is getting true also I am not able to check and unchecked checkbox when value is true checkbox not check when value it's true why it's happening not sure also I am trying to set onChange but getting confused how to set onchange
value coming from backend
NameMatch: true
Match: true
SearchForm component
export default function SearchForm() {
const { setRules, rules, fetchSearch } = useRule();
const [changeRuleSettings, setChangeRuleSettings] = React.useState(false);
React.useEffect(() => {
fetchSearchRule();
}, []);
const handleCheckBoxChange = (e) => {
setRules((prevRules) => ({
...prevRules,
[e.target.name]: !prevState[e.target.checked],
}));
};
return (
<Grid item xs={4}>
<Box className={classes.formText}>
<Checkbox
style={style}
name="NameMatch"
checked={rules.NameMatch}
color="primary"
onChange={handleCheckBoxChange}
/>
<Typography variant="subtitle2" noWrap={true}>
Match
</Typography>
</Box>
</Grid>
)
}
If I am understanding the question correctly, you are asking what should the onChange function exactly do. So basically you need to switch the values of the property, that the Checkbox is dependent on. For example, if the backend returns :
exactNameMatch: true
partialNameMatch: true
Then for the first checkbox, when it is clicked, the value of exactNameMatch should be set to false. An example implementation would look something like this:
onChange={(e) => setRules((prevRules) => ({...prevRules, exactNameMatch: !prevRules.exactNameMatch}))}
For the second checkbox the onChange will be identical, with the small difference, that instead of exactNameMatch you would use partialNameMatch.
Another possibility is to implement the handleCheckBoxChange function like so:
const handleCheckBoxChange = (e) => {
setRules((prevRules) => ({...prevRules, [e.target.name]: !prevRules[e.target.name]}))
}
Where e.target.name is respectively exactNameMatch or partialNameMatch, based on the values you have passed to the name properties of the Checkbox component. Hope I managed to understand your problem correctly and actually help you!

How can I keep specific checkboxes checked even when they change its index?

So I have a component which includes a checkbox. This:
const MyComp: FC<Props> = ({ question, questionIdx }) => {
const [isCheck, setCheck] = useState<{ [key: number]: boolean }>({ 0: false })
const handleCheckboxes = (idx: number, value: boolean): void => setCheck({ [idx]: value })
return (
<Box>
<Checkbox
checked={isCheck[questionIdx]}
onChange={() => handleCheckboxes(questionIdx, !isCheck[questionIdx])}
/>
</Box>
)
}
I have to call that component in a place where it will be rendered within an array with a big length. Right now I am checking/unchecking the checkboxes by its index. The thing is that this component is draggable/droppable so its index will change constantly so they will lost the check.
What do you recommend me doing to fix this issue?
One option would be to generate an ID to use instead of the index.
If you don't have a server-side ID (or don't want one), you could use a package like nanoid to generate one when the checkboxes are created.
import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"
Note: make sure to not generate a new ID on each render (see https://github.com/ai/nanoid#react).

How to set checked items to React state array

I am building a quiz in a React app, and I am working on a specific question that allows the user to select multiple options via checkboxes. So far, for other questions that are only radio button options, I have been able to set the selected answer to the state just fine. But I am getting stuck on how I would do this for a question that is checkboxes. I need the state to be updated each time a checkbox is checked or unchecked. The code for this checkbox question is:
import React, { useState, useEffect } from "react";
import { connect } from "frontity";
import { GridWrap, GridRow, GridColumn } from "emotion-flex-grid";
import QuizCheckbox from "/src/components/quiz-checkbox";
import QuestionCommonContainer from "../question-common.style";
const Question3 = ({ actions }) => {
const [checkedItems, setCheckedItems] = useState({});
const checkboxes = [
{
key: "checkBox1",
label: "Back",
},
{
key: "checkBox2",
label: "Side",
},
{
key: "checkBox3",
label: "Stomach",
},
];
const handleChange = (event) => {
setCheckedItems({
...checkedItems,
[event.target.name]: event.target.checked,
});
};
return (
<QuestionCommonContainer>
<GridWrap>
<GridColumn px={["l"]}>
<GridRow wrap="wrap" justify="center">
<GridColumn width={[12, 12, 12, 4]}>
{checkboxes.map((item, id) => (
<GridColumn pb={"l"} key={id}>
<QuizCheckbox
name={item.label}
label={<h5>{item.label}</h5>}
selected={checkedItems[item.label]}
onChange={handleChange}
/>
</GridColumn>
))}
</GridColumn>
</GridRow>
</GridColumn>
</GridWrap>
</QuestionCommonContainer>
);
};
export default connect(Question3);
I am thinking that I need to either alter the handleChange function, but I am not completely sure if that's right or how I would need to change it. Any help would be greatly appreciated.
UPDATE:
The first two comments helped. I think I am very tired lol. I see that the items are being added to state, but with a little glitch:
So I have 3 checkboxes. I added console.log(checkedItems) to the handleChange function so I can watch the behavior in the console.
I go to make my first selection and the console returns Object { } ... an empty object, even though I have selected one item. I try to select a second item and the console returns
{
"Side": true
}
which was the second selection. I am not sure why the first selection is not being included in the returned object.
Try this fix:
const handleChange = (event) => {
const name = event.target.name;
setCheckedItems(prevState => ({
...prevState,
[name]: !prevState[name],
}));
};
I’m not entirely sure what’s wrong with your code, it looks alright to me. But if anything is broken, chances are the lib you use "#baalspots/tmm-theme/src/components/quiz-checkbox" is not behaving properly.
The above fix try to mitigate misbehavior of lib. If it doesn’t help, then you need to post more details. What exactly is broken? Any error message? Plus you need to post link to an online playground of you code somewhere, like codesandbox, so people can experiment.
Ah I guess I've debugged enough now to answer my own question. The handleChange function was just fine, and I realized that I was not getting correct console logs because I had the console.log() in the wrong place.

Categories

Resources