I am having an issue with setting the initial value of a radio group (from a value passed into the function) like this:
export default function SportTeamRanking({team, ...props}) {
So right now I have a Radio Group with 4 radio buttons and the value I want to pre-select or check is contained in the team object specified above (something like team.ranking). This value is passed from a class component into this function. So let's say the team object had a team.ranking value of "rank2" so ideally the Rank 2 radio button would be pre-selected when the function loads.
I have an initial state:
const [value, setValue] = React.useState("");
I've tried setting the React.useState(team.ranking) but still no button is selected.
I also tried using the useEffect and run it only once:
useEffect(() => {
setValue(team.ranking)
}, []);
but with no luck. I tried console logging early on and seems like the team object is still empty while all of this code is already executed?
The goal of this function is to pre-load any saved value that might have been set before (from the team object passed in), then update to a new ranking value if required and save.
For extra info here is the radio group code in the return body:
<FormControl component="fieldset">
<FormLabel component="legend">Team Ranking</FormLabel>
<RadioGroup aria-label="ranking" name="ranking1" value={value} onChange={handleChange}>
<FormControlLabel value="rank1" control={<Radio />} label="Rank 1" />
<FormControlLabel value="rank2" control={<Radio />} label="Rank 2" />
<FormControlLabel value="rank3" control={<Radio />} label="Rank 3" />
<FormControlLabel value="rank4" control={<Radio />} label="Rank 4" />
</RadioGroup>
</FormControl>
And the onChange is simply:
const handleChange = (event) => {
setValue(event.target.value);
};
Any suggestions are appreciated, thanks!
As you have mentioned that the value is passed from a class component to a function component, see if the below code answers your question.
function FormControlLabelPosition(props) {
const onChange = (e) => props.onChange(e.target.value)
return (
<FormControl component="fieldset">
<FormLabel component="legend">Team Ranking</FormLabel>
<RadioGroup aria-label="ranking" name="ranking1" value={props.value} onChange={onChange}>
<FormControlLabel value="rank1" control={<Radio />} label="Rank 1" />
<FormControlLabel value="rank2" control={<Radio />} label="Rank 2" />
<FormControlLabel value="rank3" control={<Radio />} label="Rank 3" />
<FormControlLabel value="rank4" control={<Radio />} label="Rank 4" />
</RadioGroup>
</FormControl>
);
}
export default class Parent extends React.Component {
state = {
value: "rank4"
}
handleChange = (val:string) => this.setState({value: val})
render() {
return (<FormControlLabelPosition value={this.state.value} onChange={this.handleChange}/>)
}
}
Try it out here: https://codesandbox.io/s/material-demo-4ilxb
Related
hello I'm trying to show and hide specific elements with checkbox material and what is happening now is when one checkbox is checked all the hidden divs are showing up.
You can see the problem here: https://stackblitz.com/edit/react-1ecdqb?file=demo.tsx
edit: I know that I need more variables in the state but I ask if there is a way to do it without state for each checkbox because there are gonna be 10 more checkboxes
const UninstallView = () => {
const [isChecked, setIsChecked] = useState(false);
const handleChange = event => {
if (event.target.checked) {
setIsChecked(true);
}
else {
setIsChecked(false);
}
}
return (
<div>
<FormGroup>
<FormControlLabel control={<Checkbox onChange={handleChange} />} label="simple isn't what I expected" />
{isChecked ? <TextField
id="filled-multiline-static"
label="What did you expect from simple?"
multiline
rows={4}
defaultValue=""
variant="filled"
/>
: '' }
</FormGroup>
<FormGroup>
<FormControlLabel control={<Checkbox onChange={handleChange} />} label="simple isn't working correctly" />
{isChecked ?
<div>
<h1>hello</h1>
</div>
: '' }
</FormGroup>
</div>
);
You are sharing 1 state across 2 checkboxes. You should have a separate state that holds the state for each checkbox.
This code may help:
const UninstallView = () => {
const [isFirstChecked, setIsFirstChecked] = useState(false);
const [isSecondChecked, setIsSecondChecked] = useState(false);
return (<div>
<FormGroup>
<FormControlLabel
control={<Checkbox onChange={() => setIsFirstChecked(!isFirstChecked)}/>}
label="simple isn't what I expected"/>
{isFirstChecked ? <TextField
id="filled-multiline-static"
label="What did you expect from simple?"
multiline
rows={4}
defaultValue=""
variant="filled"
/> : ''}
</FormGroup>
<FormGroup>
<FormControlLabel
control={<Checkbox onChange={() => setIsSecondChecked(!isSecondChecked)}/>}
label="simple isn't working correctly"/>
{isSecondChecked ? <div>
<h1>hello</h1>
</div> : ''}
</FormGroup>
</div>);
}
Sandbox example
how to let a textfiled back to initial status.
enter image description here
step 1 :
when i choice the 'Others ' and give a value to the textfield .
enter image description here
step 2:
now i change 'Others' to '3KG' option. I hope the textfield back to initial status .
with the code as follow , just empty the value,but the title not back to initial position. ...
const handleWeightChange = (event) => {
if(event.target.value === 'a4') {
totalWeightRef.current.focus();
}
else {
totalWeightRef.current.value = null;
}
};
<RadioGroup row aria-label="type" name="name" className={classes.weightradio} onChange={handleWeightChange}>
<FormControlLabel value="a1" control={<Radio />} label="1KG" />
<FormControlLabel value="a2" control={<Radio />} label="2KG" />
<FormControlLabel value="a3" control={<Radio />} label="3KG" />
<FormControlLabel value="a4" control={<Radio />} label="Others" />
</RadioGroup>
<TextField id="totalWeight" label="重量KG" inputRef={totalWeightRef} />
enter image description here
I'm currently having problems when using Formik with MaterialUI forms. Specifically,
I am having trouble passing Formik input values in nested forms using Material UI and having a small issue where Formik.handleChange is changing the value from number to string.
I have multiple forms that are split with Stepper component in Material UI. Since I am still learning, I tried to wrap Formik on one of the steps (later on I am going to wrap the whole stepper). Here's how it looks:
{activeStep === 0 && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{formik => (
<form onSubmit={formik.handleSubmit}>
<div className="col-xl-6">
<Portlet fluidHeight>
<PortletHeader title="Incident Type" />
<PortletBody>
<IncidentSelect formik={formik} />
</PortletBody>
</Portlet>
</div>
</form>
)}
</Formik>
)}
The problem is inside the IncidentSelect form, Formik handleChange does not seem to change the selected radioButton. When I inspected with React Developer Tools in Chrome it seems that Formik.handleChange is changing the value from 0 to "0". How do I fix this?
Also, following the tutorial, I'm unsure how I can abstract my components? Note that DateTimePicker is using material-ui/pickers. I'm not sure how I am going to pass the value to Formik.
Any help is appreciated.
Thanks
function IncidentSelect(props) {
const [value, setValue] = React.useState("female");
const handleRadioChange = e => {
console.log(props.formik.getFieldProps("incidentType"));
setValue(e.target.value);
};
return (
<>
<FormControl component="fieldset">
<FormLabel component="legend" required>
Incident Type:
</FormLabel>
<RadioGroup
aria-label="Incident Type"
name="incidentType"
value={value}
onChange={handleRadioChange}
{...props.formik.getFieldProps("incidentType")}
>
<FormControlLabel
value={0}
control={<Radio />}
label="Injury To Guest"
/>
<FormControlLabel
value={1}
control={<Radio />}
label="Injury To Worker"
/>
<FormControlLabel
value={2}
control={<Radio />}
label="Incident / Accident"
/>
<FormControlLabel
value={3}
disabled
control={<Radio />}
label="OSH / Kids Camp"
/>
</RadioGroup>
</FormControl>
<DateTimePicker
label="Signed Date"
variant="outlined"
className={classes.margin}
value={selectedDate}
onChange={handleDateChange}
/>
</>
);
}
As stated in the tutorial, it is easier to abstract the component that you want to use. Giving you chances to reuse them later in your application and more readable code.
Formik provides you with useFields API to get the props of the field via hooks. useFields is looking for the name props of your component to find the corresponding field.
Thus RadioGroup from MaterialUI can be extracted as follows:
export const IncidentRadioButton = ({ options, ...props }) => {
const [field, meta] = useField(props);
return (
<>
<RadioGroup {...field} {...props}>
{options.map((option, index) => (
<FormControlLabel
control={<Radio />}
label={option.name}
value={option.value}
key={index}
/>
))}
</RadioGroup>
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
};
Then you can use the options prop to put your data accordingly
EDIT: I forgot to say that the onChange on the inputs doesn't work, how do I tell to onChange it's the state value inside the group?
I started to learn React few weeks ago. I want to SUM the group state values and every time it changes the total value changes too. I'm using material ui to create the form. The problem is if I change the state value to int the FormControlLabel value won't work... as a string the total doesn't work as expectable. And how I can handle the total change everytime I change the option?
Thanks in advance.
class App extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
total: 0,
group: {
feedingGroup: "3",
bathingA: "3",
bathingB: "3",
}
}
}
componentDidMount() {
this.setState({ total: this.calculateTotal(this.state.group) });
}
calculateTotal = (values) => {
return Object.entries(values).reduce((finalValue, [key, value]) => {
return finalValue + value;
}, 0);
}
handleSubmit(event) {
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<FormControl component="fieldset" error="" className="">
<RadioGroup aria-label="quiz" name="quiz" value={this.state.group.feedingGroup}
onChange={(value) => { this.setState({ feedingGroup: (value.target.value) }); }}>
<FormControlLabel value="0" control={<Radio />} label="Op1" />
<FormControlLabel value="1" control={<Radio />} label="Op2" />
<FormControlLabel value="2" control={<Radio />} label="Op3" />
<FormControlLabel value="3" control={<Radio />} label="Op4" />
</RadioGroup>
<RadioGroup aria-label="quiz" name="quiz" value={this.state.group.bathingA}
onChange={(value) => { this.setState({ bathingA: (value.target.value) }); }}>
<FormControlLabel value="0" control={<Radio />} label="Op1" />
<FormControlLabel value="1" control={<Radio />} label="Op2" />
<FormControlLabel value="2" control={<Radio />} label="Op3" />
<FormControlLabel value="3" control={<Radio />} label="Op4" />
</RadioGroup>
<Button type="submit" variant="outlined" color="primary" className="">
Check Answer
</Button>
<FormHelperText>{this.state.total}</FormHelperText>
</FormControl>
</form>
);
}
}
export default App;
How about casting the value:
calculateTotal = (values) => {
return Object.entries(values).reduce((finalValue, [key, value]) => {
return finalValue + Number(value);
}, 0);
}
I wish to be able to uncheck radio buttons, Idea is like this: if I click on some radio button, it is going to be checked, if I click on another field, this another field is going to be checked instead BUT if I click on field which is already checked, I wish to uncheck it so all fields are empty. I tried to catch the moment of being checked or unchecked but seems like opposite to checkboxes, Radio buttons don't have this field. does anyone has idea how to achieve that?
setTests = (key, e) => {
console.log(e.checked)
if (e.checked) {
// this.setState({[key]: null})
console.log('works')
}
}
RadioGroup
value={this.state.test_mode}
style={{ display: "block" }}
onChange={e => this.setTests({ "test_mode", e.target })}
>
<FormControlLabel value="before" control={<Radio color="primary"/>} label="before tests" />
<FormControlLabel value="progressing" control={<Radio color="primary"/>} label="progressing" />
<FormControlLabel value="done" control={<Radio color="primary"/>} label="done" />
</RadioGroup>
Below is an example of how to do this. Instead of using the onChange of the RadioGroup, you use the onClick event of the Radio. If the new value matches the current value in state, then set the value to empty string.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Radio from "#material-ui/core/Radio";
import RadioGroup from "#material-ui/core/RadioGroup";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import FormControl from "#material-ui/core/FormControl";
import FormLabel from "#material-ui/core/FormLabel";
const useStyles = makeStyles(theme => ({
root: {
display: "flex"
},
formControl: {
margin: theme.spacing(3)
},
group: {
margin: theme.spacing(1, 0)
}
}));
export default function RadioButtonsGroup() {
const classes = useStyles();
const [value, setValue] = React.useState("female");
function handleClick(event) {
if (event.target.value === value) {
setValue("");
} else {
setValue(event.target.value);
}
}
return (
<div className={classes.root}>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">Gender</FormLabel>
<RadioGroup
aria-label="gender"
name="gender1"
className={classes.group}
value={value}
>
<FormControlLabel
value="female"
control={<Radio onClick={handleClick} />}
label="Female"
/>
<FormControlLabel
value="male"
control={<Radio onClick={handleClick} />}
label="Male"
/>
<FormControlLabel
value="other"
control={<Radio onClick={handleClick} />}
label="Other"
/>
</RadioGroup>
</FormControl>
</div>
);
}