I'm building an app in React using Material-UI components. In one window there are four true/false choices represented by radio buttons. On render the window renders as expected, but clicking on a non-default value only causes the default radio button to lose its fill, and does not fill the clicked-on choice.
Example: if User can mint defaults to false, then false will be filled on render, but clicking true for that option won't fill true (though false will become 'unfilled').
The actual state does change, though. So this seems to only be a render problem, not an underlying problem with the state.
Here's the code (I'll try to take out the unnecessary parts):
const UpdateDefaultRole = props => {
const [open, setOpen] = React.useState(false);
const [canMint, setCanMint] = React.useState(false);
const [canBurn, setCanBurn] = React.useState(false);
const [payFee, setPayFee] = React.useState(true);
const [paySplit, setPaySplit] = React.useState(true);
const handleMintChange = e => setCanMint(e.target.value);
const handleBurnChange = e => setCanBurn(e.target.value)
const handleFeeChange = e => setPayFee(e.target.value)
const handleSplitChange = e => setPaySplit(e.target.value)
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Update Default Role
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Update Default Role</DialogTitle>
<DialogContent>
<DialogContentText>
Please enter the new permissions for the default role.<br/> WARNING: Be careful!
Improper use of this function is dangerous!
</DialogContentText>
<FormControl component="fieldset" style={{padding: "20px", margin: "10px"}}>
<FormLabel component="legend">User Can Mint Tokens:</FormLabel>
<RadioGroup aria-label="mint" name="can-mint" value={canMint} onChange={handleMintChange}>
<FormControlLabel value={true} control={<Radio />} label="True" />
<FormControlLabel value={false} control={<Radio />} label="False" />
</RadioGroup>
</FormControl>
<FormControl component="fieldset" style={{padding: "20px", margin: "10px"}}>
<FormLabel component="legend">User Can Burn Tokens:</FormLabel>
<RadioGroup aria-label="burn" name="can-burn" value={canBurn} onChange={handleBurnChange}>
<FormControlLabel value={true} control={<Radio />} label="True" />
<FormControlLabel value={false} control={<Radio />} label="False" />
</RadioGroup>
</FormControl>
<FormControl component="fieldset" style={{padding: "20px", margin: "10px"}}>
<FormLabel component="legend">User Must Pay Fee:</FormLabel>
<RadioGroup aria-label="fee" name="pay-fee" value={payFee} onChange={handleFeeChange}>
<FormControlLabel value={true} control={<Radio />} label="True" />
<FormControlLabel value={false} control={<Radio />} label="False" />
</RadioGroup>
</FormControl>
<FormControl component="fieldset" style={{padding: "20px", margin: "10px"}}>
<FormLabel component="legend">User Must Pay Split:</FormLabel>
<RadioGroup aria-label="split" name="pay-split" value={paySplit} onChange={handleSplitChange}>
<FormControlLabel value={true} control={<Radio />} label="True" />
<FormControlLabel value={false} control={<Radio />} label="False" />
</RadioGroup>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={submitUpdateDefaultRole} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
</div>
);
}
I've tried to follow the code on Material-UI's site as loyally as possible, but I'm obviously messing up somewhere.
Thank you for taking the time to read my question!
Probably it's a string and not a boolean and that is causing the issue. Try as:
const handleMintChange = e => setCanMint(e.target.value === 'true');
Related
I trying to make a crud table using material UI,but as my first react project I have some issues.I don't know how to make the edit.I take the data to the edit-modal but I don't know how to make the update in the table.
export default function ResponsiveDialog({dataParent1,dataParent2}) {
const [open, setOpen] = React.useState(false);
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('md'));
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button variant="outlined" color='success' onClick={handleClickOpen}>
Edit
</Button>
<Dialog
fullScreen={fullScreen}
open={open}
onClose={handleClose}
aria-labelledby="responsive-dialog-title"
>
<DialogTitle id="responsive-dialog-title">{"Edit Post"}
</DialogTitle>
<DialogContent>
<Divider variant="middle"/>
<DialogContentText>
Title
</DialogContentText>
<TextField id="outlined-basic" defaultValue={dataParent1} style = {{width: 500}} variant="outlined" />
<DialogContentText>
Description
</DialogContentText>
<TextField id="outlined-basic" multiline rows={4} defaultValue={dataParent2} style = {{width: 500}} variant="outlined" />
</DialogContent>
<DialogActions>
<Button autoFocus variant="outlined" color='error' onClick={handleClose}>
Exit
</Button>
<Button color='success' variant="outlined" onClick={handleClose} autoFocus>
Save Changes
</Button>
</DialogActions>
</Dialog>
</div>
);}
Codesandbox:
https://codesandbox.io/s/angry-panka-7wd7ye?file=/src/App.js
I'm facing a problem that I seem to know but I don't know. I made "State" by [checked]. There is a problem that if you turn this to the map and click only one checkbox, the whole is clicked. So I put {cked[idx]} to solve it, but my senior advised me that there was a problem with the code.
Because [checked] is in the form of "Boolean" and idx is "number".
And input has a 'disabled' attribute. I know that this is also deactivated as a whole because I put 'checked' in. By the way, I want each of them to be deactivated when I press the check box. But I don't know how. I searched for stack overflow, but I couldn't find a similar case to mine.
I believe you guys can help me.
const [checked, setChecked] = useState(false);
.
.
.
{keys &&
keys?.map((item: any, idx: number) => {
return (
<div key={idx}>
<Box component="form" className={classes.Box}>
<div className={classes.typeText}>{item.columnName}</div>
<div className={classes.flexWrap}>
<TextField
id="outlined-basic"
label={item.type}
variant="outlined"
className={classes.filled}
disabled={checked} //hear❗️
/>
<FormControlLabel
value="end"
control={
<Checkbox
className={classes.checkBox}
color="primary"
checked={checked[idx]} //hear❗️
onChange={handleCheckboxChange}
/>
}
label="Null"
labelPlacement="end"
/>
</div>
</Box>
</div>
);
})}
Move your textfield and checkbox to a different component so each one can have its own state.
function CheckComponent = (item, handleCheckboxChange) => {
const [checked, setChecked] = useState(false);
return (
<Box component="form" className={classes.Box}>
<div className={classes.typeText}>{item.columnName}</div>
<div className={classes.flexWrap}>
<TextField
id="outlined-basic"
label={item.type}
variant="outlined"
className={classes.filled}
disabled={checked}
/>
<FormControlLabel
value="end"
control={
<Checkbox
className={classes.checkBox}
color="primary"
checked={checked}
onChange={handleCheckboxChange}
/>
}
label="Null"
labelPlacement="end"
/>
</div>
</Box>
);
}
Then render as many as you need
{keys && keys?.map((item: any, idx: number) => {
<CheckComponent key={idx} item={item} handleCheckboxChange={() => console.log("clicked", idx); } />
})}
Something along these lines.
I have problem with Material-ui Autocomplete:
import Autocomplete from "#material-ui/lab/Autocomplete";
I am using then in:
<Autocomplete
id="checkboxes-tags-demo"
autoComplete={false}
options={states}
disableCloseOnSelect
getOptionLabel={(option) => option.name}
onChange={onStateChange}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.name}
</React.Fragment>
)}
style={{ width: "100%" }}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="State"
placeholder="Enter state"
/>
)}
/>
Nothing important in this code but I face this issue:
I am getting the browser suggestion. How I can remove it ?
As mentioned in this SO question, you should add autoComplete="off" to your TextField.
I have a React button that is a form and I intend to submit the form when I click on the Button. Unfortunately, when I click on the button, nothing happens and the page only reloads. Even when I just try to output to the console nothing happens but when you check the network, you could see an action being performed.
Below is my code which is just a simple login form and a functionality that validates user input. I need the button to call the function handleSubmit when I click on the button.
export default function Signin() {
const classes = useStyles();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [usernameError, setUsernameError] = useState('');
const [passwordError, setPasswordError] = useState('');
const validateUsername = () => {
setUsernameError(username.length > 3 ? null : 'Username must be longer than 3 characters')
}
const validatePassword = () => {
setPasswordError(password.length > 7 ? null : 'Password must be longer than 8 characters')
}
const handleSubmit = () => {
//const { history } = this.props;
console.log('testing button')
}
return (
<Grid
className={classes.root}
component="main"
container
>
<CssBaseline />
<Grid
className={classes.image}
item
md={4}
sm={7}
xs={false}
/>
<Grid
component={Paper}
elevation={6}
item
md={8}
sm={5}
square
xs={12}
>
<MuiThemeProvider theme={theme}>
<div className={classes.paper}>
<img
alt="logo"
src={process.env.PUBLIC_URL + '/images/....'}
/>
<Typography
component="h1"
style={{ color: '#E60000' }}
variant="h5"
>
Sign In
</Typography>
<form
className={classes.form}
noValidate
>
<Grid
container
spacing={2}
>
<Grid
item
xs={3}
/>
<Grid
item
xs={6}
>
<TextField
autoComplete="username"
className={`form-control ${usernameError ? 'is-invalid' : ''}`}
fullWidth
id="username"
label="Enter Username"
margin="normal"
name="username"
onBlur={validateUsername}
onChange={e => setUsername(e.target.value)}
required
value={username}
variant="outlined"
/>
<div className={classes.invalidFeedback}>{usernameError}</div>
</Grid>
<Grid
item
xs={3}
/>
<Grid
item
xs={3}
/>
<Grid
item
xs={6}
>
<TextField
autoComplete="current-password"
className={`form-control ${passwordError ? 'is-invalid' : ''}`}
fullWidth
id="password"
label="Password"
margin="normal"
name="password"
onBlur={validatePassword}
onChange={e => setPassword(e.target.value)}
required
type="password"
value={password}
variant="outlined"
/>
<div className={classes.invalidFeedback}>{passwordError}</div>
</Grid>
<Grid
item
xs={3}
/>
<Grid
item
xs={3}
/>
<Grid
item
xs={3}
>
<Button
className={classes.submit}
color="primary"
fullWidth
onClick={handleSubmit}
type="submit"
variant="contained"
>
Sign In
</Button>
<Grid
item
xs={3}
/>
</Grid>
</Grid>
<Grid container>
<Grid
item
xs={3}
/>
<Grid
item
xs={3}
>
<Link
className={classes.link}
to={`${process.env.PUBLIC_URL}/passwordreset`}
variant="body2"
>
<Button
className={classes.submit}
color="primary"
fullWidth
type="submit"
variant="text"
>
Forgot Password
</Button>
</Link>
</Grid>
<Grid item>
<Link
className={classes.link}
to={`${process.env.PUBLIC_URL}/signup`}
variant="body2"
>
<Button
className={classes.submit}
color="primary"
fullWidth
type="submit"
variant="text"
>
New User?
</Button>
</Link>
</Grid>
</Grid>
<Box mt={5}>
<SigninTrouble />
</Box>
</form>
</div>
</MuiThemeProvider>
</Grid>
</Grid>
);
}
Since you are using Form you need to handle submit method in form instead of your button. Because your button type is submit which triggers the onSubmit event in form.
<form onSubmit={handleSubmit}>
<button type="submit"></button>
</form>
If you want to make an async call you need to stop normal event cycle for your form.
const handleSubmit = (event) => {
// stop redirect
event.preventDefault();
console.log('testing button');
// here you can make your async call
}
In order to use a button with type submit it has to be nested within an html form tag and handle the submission with the onsubmit attribute.
Onother option is to attack an onClick event handler to your button and have your submission logic there.
Here is very simple example:
import React, {useEffect, useState} from 'react';
function Example(props) {
function clickHandler() {
alert('hi');
}
return (
<div>
<button onClick={clickHandler}>Click here</button>
</div>
);
}
export default Example;
Total newbie to React.I'm building a multi-step form with a step counter in the State and Switch statement in the original render method to show what component is displayed. Similar to the tutorial on https://www.youtube.com/watch?v=zT62eVxShsY. I'm also storing all the values entered by the user in the state via props.
There's a page where the user is meant to pick a rating from 1,2,3 from multiple radioGroups to select values from the corresponding question. Like a survey! And these radioGroups change depending on which form the user decides to fill out.
I want to know the best way to store the values from the radioGroups into the state so I can destructure them in later stages/steps.
So far I've got the following code(basically just the UI). I've removed most of the radioGroups to simplify the code for this purpose, there are hundreds to be added.
Pardon the poor indentation below copy-paste was a pain somehow
import React, { Component } from "react";
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import AppBar from "material-ui/AppBar/AppBar";
import { makeStyles } from "#material-ui/core/styles";
import Grid from "#material-ui/core/Grid";
import RaisedButton from "material-ui/RaisedButton";
import RadioGroup from "#material-ui/core/RadioGroup";
import Radio from "#material-ui/core/Radio";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import ActionFavorite from "material-ui/svg-icons/action/favorite";
import ActionFavoriteBorder from "material-ui/svg-icons/action/favorite-border";
export class LessonOne extends Component {
constructor(props) {
super(props);
this.state = { value: 0 };
}
continue = e => {
e.preventDefault();
this.props.nextStep();
};
back = e => {
e.preventDefault();
this.props.prevStep();
};
handleChange = (event, index, value) => this.setState({ value });
render() {
return (
<MuiThemeProvider>
<React.Fragment>
<AppBar title="Select Ratings" />
<div style={{ padding: 50 }}>
<Grid container spacing={1}>
<Grid item xs={12}>
<h1>Lesson 01_105CPL(H) Effects of Controls</h1>
</Grid>
<Grid item xs={12}>
<h3>Lesson Content (Elements & Performance Criteria)</h3>
</Grid>
<Grid item xs={7} style={{ marginTop: 32 }}>
Condition 1
</Grid>
<Grid item>
<RadioGroup
name="C2.1(a)"
defaultValue={this.formRating({ rate })}
style={{ display: "flex", flexDirection: "row" }}
>
<FormControlLabel
value="1"
control={<Radio color="primary" />}
label="1"
labelPlacement="Top"
/>
<FormControlLabel
value="2"
control={<Radio color="primary" />}
label="2"
labelPlacement="Top"
/>
<FormControlLabel
value="3"
control={<Radio color="primary" />}
label="3"
labelPlacement="Top"
/>
<FormControlLabel
value="N/A"
control={<Radio color="primary" />}
label="N/A"
labelPlacement="Top"
/>
</RadioGroup>
</Grid>
<Grid item xs={7} style={{ marginTop: 10 }}>
{" "}
Condition 2
</Grid>{" "}
<Grid item>
<RadioGroup
name="C2.2(b)"
defaultValue="3"
style={{ display: "flex", flexDirection: "row" }}
>
<FormControlLabel
style={{ marginLeft: 16 }}
value="1"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="2"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="3"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="N/A"
control={<Radio color="primary" />}
/>{" "}
</RadioGroup>
</Grid>
<Grid item xs={7} style={{ marginTop: 10 }}>
Condition 3
</Grid>{" "}
<Grid item>
<RadioGroup
name="C2.2(a)"
defaultValue="3"
style={{ display: "flex", flexDirection: "row" }}
>
<FormControlLabel
style={{ marginLeft: 16 }}
value="1"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="2"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="3"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="N/A"
control={<Radio color="primary" />}
/>{" "}
</RadioGroup>
</Grid>
<Grid item xs={7} style={{ marginTop: 10 }}>
{" "}
Condition 4{" "}
</Grid>{" "}
<Grid item>
<RadioGroup
name="H1.3(c)"
defaultValue="3"
style={{ display: "flex", flexDirection: "row" }}
>
<FormControlLabel
style={{ marginLeft: 16 }}
value="1"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="2"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="3"
control={<Radio color="primary" />}
/>
<FormControlLabel
style={{ marginLeft: 16 }}
value="N/A"
control={<Radio color="primary" />}
/>{" "}
</RadioGroup>
</Grid>
<Grid item xs={10} style={{ marginTop: 50 }}>
<RaisedButton
label="Previous"
secondary={true}
onClick={this.back}
/>
</Grid>
<Grid item xs={2} style={{ marginTop: 50 }}>
<RaisedButton
label="Next"
primary={true}
onClick={this.continue}
/>
</Grid>
</Grid>
</div>
</React.Fragment>
</MuiThemeProvider>
);
}
}
export default LessonOne;
For every RadioGroup you need to have a onChange event handler, also you need to simplify the name of RadioGroup (don't use any special character's except (_) underscore), it is good to use camelCase name.
<RadioGroup
name="C2_1_a" //Simplifies name, you can change to whatever you want
defaultValue={this.formRating({ rate })}
style={{ display: "flex", flexDirection: "row" }}
onChange={this.radioHandler} //onChange handler
>
// Your radio buttons
</RadioGroup>
The the radioHandler function should be,
radioHandler = (e) =>{
this.setState({[e.target.name]:e.target.value});
}
Accessing of selected radio button,
{this.state.C2_1_a}