I am trying to create a sample login form in React using material-ui through graphql mutation. But the login form is not working as expected.
i am able to log in only some times, but even in those times, I do not see any data being passed via loginMutation props in console. Could some one please tell me what am i doing wrong here?
Here is the Login Component that I am trying to create
import React, { Component } from 'react';
import { AUTH_TOKEN } from '../constants';
import { graphql, compose } from 'react-apollo';
import {LOGIN_MUTATION} from "../gql/loginGQL";
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField'
import PropTypes from 'prop-types';
import Avatar from '#material-ui/core/Avatar';
import CssBaseline from '#material-ui/core/CssBaseline';
import FormControl from '#material-ui/core/FormControl';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
//import Input from '#material-ui/core/Input';
//import InputLabel from '#material-ui/core/InputLabel';
import LockIcon from '#material-ui/icons/LockOutlined';
import Typography from '#material-ui/core/Typography';
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
layout: {
width: 'auto',
display: 'block', // Fix IE11 issue.
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE11 issue.
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
});
class Login extends Component {
state = {
email: '',
password: '',
errors: null
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<main className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockIcon />
</Avatar>
<Typography variant="headline">Sign in</Typography>
<form className={classes.form}>
<FormControl margin="normal" required fullWidth>
<TextField
id='email'
value={this.state.email}
onChange={e => this.setState({ email: e.target.value })}
type='text'
label='Your email address'
/>
</FormControl>
<FormControl margin="normal" required fullWidth>
<TextField
id='password'
value={this.state.password}
onChange={e => this.setState({ password: e.target.value })}
type='password'
label='Password'
/>
</FormControl>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
id="submit"
type="submit"
fullWidth
variant="raised"
color="primary"
className={classes.submit}
onClick={() => this._confirm()}
>
Sign in
</Button>
</form>
</Paper>
</main>
</React.Fragment>
);
}
_confirm = async () => {
const { email, password } = this.state
try{
const result = await this.props.loginMutation({
variables: {
email,
password,
},
});
console.log(result); // Here no data is being displayed !
const { jwt } = result.data.signInUser;
this._saveUserData(jwt);
this.props.history.push(`/`)
} catch(error) {
const errors = error.graphQLErrors.map(error => error.message);
this.setState({ errors });
}
}
_saveUserData = (token) => {
localStorage.setItem(AUTH_TOKEN, token)
}
}
Login.propTypes = {
classes: PropTypes.object.isRequired,
};
export default compose(
graphql(LOGIN_MUTATION, { name: 'loginMutation' }),
withStyles(styles),
)(Login)
The error was being occurred due to the Button tag. Although, it had type="submit", the form was still not being submitted due to which the input data was not being appearing in the "_confirm". I am wondering why, I must have missed something here.
For now, I created a div tag inside the Button and added onClick handler there instead of Button tag.
So, the edited code just have this small change
<Button color="primary" className = {classes.submit} variant="raised" fullWidth>
<div className="test" onClick={() => this._confirm()} >
Sign in
</div>
</Button>
Here is the full source code:
import React, { Component } from 'react';
import { AUTH_TOKEN } from '../constants';
import { graphql, compose } from 'react-apollo';
import {LOGIN_MUTATION} from "../gql/loginGQL";
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField'
import PropTypes from 'prop-types';
import Avatar from '#material-ui/core/Avatar';
import CssBaseline from '#material-ui/core/CssBaseline';
import FormControl from '#material-ui/core/FormControl';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
//import Input from '#material-ui/core/Input';
//import InputLabel from '#material-ui/core/InputLabel';
import LockIcon from '#material-ui/icons/LockOutlined';
import Typography from '#material-ui/core/Typography';
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
layout: {
width: 'auto',
display: 'block', // Fix IE11 issue.
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE11 issue.
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
});
class Login extends Component {
state = {
email: '',
password: '',
errors: null
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<main className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockIcon />
</Avatar>
<Typography variant="headline">Sign in</Typography>
<form className={classes.form}>
<FormControl margin="normal" required fullWidth>
<TextField
id='email'
value={this.state.email}
onChange={e => this.setState({ email: e.target.value })}
type='text'
label='Your email address'
/>
</FormControl>
<FormControl margin="normal" required fullWidth>
<TextField
id='password'
value={this.state.password}
onChange={e => this.setState({ password: e.target.value })}
type='password'
label='Password'
/>
</FormControl>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
// Here is the change
<Button color="primary" className = {classes.submit}
variant="raised"fullWidth >
<div className="test" onClick={() => this._confirm()} >
Sign in
</div>
</Button>
// Change ends here
</form>
</Paper>
</main>
</React.Fragment>
);
}
_confirm = async () => {
const { email, password } = this.state
try{
const result = await this.props.loginMutation({
variables: {
email,
password,
},
});
console.log(result); // Here no data is being displayed !
const { jwt } = result.data.signInUser;
this._saveUserData(jwt);
this.props.history.push(`/`)
} catch(error) {
const errors = error.graphQLErrors.map(error => error.message);
this.setState({ errors });
}
}
_saveUserData = (token) => {
localStorage.setItem(AUTH_TOKEN, token)
}
}
Login.propTypes = {
classes: PropTypes.object.isRequired,
};
export default compose(
graphql(LOGIN_MUTATION, { name: 'loginMutation' }),
withStyles(styles),
)(Login)
Related
In my React code, along with other input types, I also have to use ToggleButtonGroup component of material-ui. This component is not directly supported by formik-material-ui and I've been trying to write a custom wrapper without much success. This is how my component looks(formikToggleButtonGroup.tsx):
import * as React from 'react';
import MuiToggleButtonGroup, {
ToggleButtonGroupProps as MuiToggleButtonGroupProps,
} from '#material-ui/lab/ToggleButtonGroup';
import { FieldProps } from 'formik';
export interface ToggleButtonGroupProps
extends FieldProps,
Omit<MuiToggleButtonGroupProps, 'name' | 'value'> {}
export function fieldToToggleButtonGroup({
field,
// Exclude Form
form,
...props
}: ToggleButtonGroupProps): MuiToggleButtonGroupProps {
return {
...props,
...field,
};
}
export default function ToggleButtonGroup(props: ToggleButtonGroupProps) {
return <MuiToggleButtonGroup {...fieldToToggleButtonGroup(props)} />;
}
ToggleButtonGroup.displayName = 'FormikMaterialUIToggleButtonGroup';`
Then, I'm trying to use it like this:
import Layout from '../../components/layout'
import Header from '../../components/header'
import ToggleButtonGroup from '../../components/formikToggleButtonGroup.tsx'
import React, { useState } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Container from '#material-ui/core/Container';
import CssBaseline from '#material-ui/core/CssBaseline';
import Grid from '#material-ui/core/Grid';
import Box from '#material-ui/core/Box';
import ToggleButton from '#material-ui/lab/ToggleButton';
import { withStyles } from '#material-ui/core/styles';
import { green, orange } from '#material-ui/core/colors';
import Radio from '#material-ui/core/Radio';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
import * as Yup from "yup";
import { Formik, Form, Field} from 'formik';
import { TextField, RadioGroup } from 'formik-material-ui';
import ToggleButtonGroup from '../../components/formikToggleButtonGroup.tsx'
import ToggleButton from '#material-ui/lab/ToggleButton';
const StyledButton = withStyles((theme)=>({
root: {
background: theme.palette.background.paper,
color: theme.palette.text.primary,
marginBottom: theme.spacing(3),
marginRight: theme.spacing(4),
borderRadius: '0',
borderWidth: '1px',
borderColor: theme.palette.divider,
borderLeft:'1px solid rgba(0, 0, 0, 0.12) !important',
padding: '0 30px',
fontSize:'0.7rem',
fontWeight:'400',
'&$selected': {
color: theme.palette.background.default,
backgroundColor: theme.palette.warning.main,
},
'&:hover': {
color: theme.palette.background.default,
backgroundColor: theme.palette.warning.light,
},
'&$selected:hover': {
color: theme.palette.background.default,
backgroundColor: theme.palette.warning.light,
},
},
selected:{
color: theme.palette.background.default,
backgroundColor: theme.palette.warning.main,
},
hover:{
color: theme.palette.background.default,
backgroundColor: theme.palette.warning.light,
},
label: {
textTransform: 'capitalize',
},
}))(ToggleButton);
export default function GetUserInfo() {
const classes = useStyles();
const [selectedValue, setSelectedValue] = useState('yes');
const handleRadioChange = (event) => {
setSelectedValue(event.target.value);
};
const handleSubmit = (evt) => {
evt.preventDefault();
alert(`Submitting Name ${name} ${phone} ${email}`)
}
const handleChange = (event, newAlignment) => {
alert(newAlignment)
if (newAlignment !== null) {
setGrade(newAlignment);
}
};
return (
<Layout title="My first Next page" meta_desc="Best online coding classes for kids & children">
<CssBaseline />
<Header />
<Container component="main" >
<Grid container spacing={4}>
<Grid item xs={12} sm={8} className={classes.boxStyle}>
<Box mx="auto" p={2}>
<Formik
initialValues={initialValues}
validate={
values => {
const errors = {};
if (!values.parentEmail) {
errors.parentEmail = 'Required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.parentEmail)
) {
errors.parentEmail = 'Invalid email address';
} else if (!values.parentName){
errors.parentName = 'Required';
} else if (!values.countryCode){
errors.countryCode = 'Required';
} else if (!values.phone){
errors.phone = 'Required';
} else if (!values.childName){
errors.childName = 'Required';
} else if (!values.grade){
errors.grade = 'Required';
} else if (!values.computer){
errors.computer = 'Required';
}
return errors;
}
}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
setSubmitting(false);
alert(JSON.stringify(values, null, 2));
}, 500);
}}
>
{({ submitForm, isSubmitting }) => (
<Form className={classes.root} autoComplete="off">
<Box>
<Field
component={TextField}
fullWidth
name="childName"
onChange={e => setChildName(e.target.value)}
required id="standard-required" label="Kid's Name"
helperText="Certificate will be issued with this name"/>
</Box>
<Box mt={3} className={classes.labelFont}>
Kid's Grade?
</Box>
<Box mt={2}>
<ToggleButtonGroup
component={ToggleButtonGroup}
mt={7}
name="grade"
//value={grade}
exclusive="true"
//onChange={handleChange}
aria-label="text alignment"
>
<StyledButton className={classes.buttonMargin}
value="1" aria-label="left aligned">
<label class="btn btn-secondary m-2 rounded">
Grade <br/> <strong>1 - 3</strong>
</label>
</StyledButton>
<StyledButton className={classes.buttonMargin} value="2" aria-label="centered">
<label class="btn btn-secondary m-2 rounded ">
Grade <br/> <strong>4 - 6</strong>
</label>
</StyledButton>
<StyledButton className={classes.buttonMargin} value="3" aria-label="right aligned">
<label class="btn btn-secondary m-2 rounded">
Grade <br/> <strong>7 - 9</strong>
</label>
</StyledButton>
<StyledButton className={classes.buttonMargin} value="4" aria-label="justified">
<label class="btn btn-secondary m-2 rounded">
Grade <br/><strong>10 - 12</strong>
</label>
</StyledButton>
</ToggleButtonGroup>
</Box>
<Button
disabled={isSubmitting}
onClick={submitForm}
//type="submit"
//onClick={handleSubmit}
fullWidth
variant="contained"
color="primary"
className={classes.submit}>Let's Start
</Button>
</Form>
)}
</Formik>
</Box>
</Grid>
</Grid>
</Container>
</Layout>
)
}
The problem I am facing is that value of ToggleButtonGroup is not getting set by Formik. It always shows the initial value.
I was able to make formik work with ToggleButtonGroup by manually handling its onChange event, and manually updating the formik instance:
export default function MyComponent() {
const formik = useFormik({
initialValues: {
roleType: 0,
},
onSubmit: async (values) => {
// send ajax request
},
});
// handler for ToggleButtonGroup
const handleRoleChange = (event: any, role: string) => {
// manually update formik
formik.setFieldValue('roleType', role);
};
return (
<form noValidate onSubmit={formik.handleSubmit}>
<ToggleButtonGroup
exclusive
id="roleType"
value={formik.values.roleType}
onChange={handleRoleChange}
>
<ToggleButton value={0}>
<PersonIcon />
<div>Role 1</div>
</ToggleButton>
<ToggleButton value={1}>
<RestaurantIcon />
<div>Role 2</div>
</ToggleButton>
</ToggleButtonGroup>
<Button type="submit">
Submit
</Button>
</form>
);
}
I am going through trouble to connectand dispatch in function based component. I know how to connect and dispatch class based component.
This is my code;
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import Button from '#material-ui/core/Button';
import SaveIcon from '#material-ui/icons/Save';
import { connect } from "react-redux";
const useStyles = makeStyles(theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
width: 200,
},
dense: {
marginTop: 19,
},
menu: {
width: 200,
},
paper: {
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
},
button: {
margin: theme.spacing(1),
},
}));
export default function TextFields() {
const classes = useStyles();
const [values, setValues] = React.useState({
ssn: '',
phone: '',
email: '',
multiline: 'Controlled',
});
const handleChange = name => event => {
setValues({ ...values, [name]: event.target.value });
};
const onSubmit = () => {
const data = {
ssn: values.ssn,
phone: values.phone,
email: values.email
}
console.log(data)
this.props.dispatch({type: 'SUBMIT', data})
}
return (
<React.Fragment>
<Grid item xs={12}>
<Paper className={classes.paper}>xs=12</Paper>
</Grid>
<form className={classes.container} noValidate autoComplete="off">
<TextField
id=""
label="SSN"
value={values.ssn}
onChange={handleChange('ssn')}
type="number"
className={classes.textField}
name='ssn'
margin="normal"
/>
<TextField
id=""
label="Phone"
value={values.phone}
onChange={handleChange('phone')}
type="number"
className={classes.textField}
name='phone'
margin="normal"
/>
<TextField
id=""
label="Email"
value={values.email}
onChange={handleChange('email')}
type="email"
className={classes.textField}
margin="normal"
name='email'
/>
<Button
onClick={() => onSubmit()}
variant="contained"
color="primary"
size="small"
className={classes.button}
startIcon={<SaveIcon />}
>
Save
</Button>
</form>
</React.Fragment>
);
}
Can anyone help me to dispatch the data or connect ? THANKS IN ADVANCE
I've created a SignIn component using Material UI's example.
import React from 'react';
import PropTypes from 'prop-types';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import FormControl from '#material-ui/core/FormControl';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import Input from '#material-ui/core/Input';
import InputLabel from '#material-ui/core/InputLabel';
import LockOutlinedIcon from '#material-ui/icons/LockOutlined';
import Paper from '#material-ui/core/Paper';
import Typography from '#material-ui/core/Typography';
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
main: {
width: 'auto',
display: 'block', // Fix IE 11 issue.
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
});
function SignIn(props) {
const { classes } = props;
return (
<main className={classes.main}>
<CssBaseline />
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form}>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="email">Email Address</InputLabel>
<Input id="email" name="email" autoComplete="email" autoFocus />
</FormControl>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="password">Password</InputLabel>
<Input name="password" type="password" id="password" autoComplete="current-password" />
</FormControl>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign in
</Button>
</form>
</Paper>
</main>
);
}
SignIn.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SignIn);
Now I want to use it in a parent component with a submit handler. But I can't figure out how to execute the handler when the form in the child component is submitted
import React, { Component } from "react";
import SignIn from "../components/SignIn";
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit = async event => {
// Do Stuff
}
render() {
return (
<div className="Login">
<SignIn onSubmit={this.handleSubmit}/>
</div>
);
}
}
Change your button in the SignIn component to call the submit handler passed in through the props like so:
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={this.props.onSubmit}
>
Sign in
</Button>
I want save value for date from this code, I use vscode and reactjs and Material package.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
width: 200,
},
});
function DatePickers(props) {
const { classes } = props;
//console.log(props)
return (
<form className={classes.container} noValidate>
<TextField
id="date"
label="Birthday"
type="date"
defaultValue="2017-05-24"
className={classes.textField}
InputLabelProps={{
shrink: true,
}}
/>
</form>
);
}
DatePickers.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(DatePickers);
In order to save the selected date, your code should look something like this,
import React, { Component } from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
const styles = theme => ({
container: {
display: "flex",
flexWrap: "wrap"
},
textField: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
width: 200
}
});
class DatePickers extends Component {
state = {
date: "2017-05-24"
};
handleChange = event => {
this.setState({ date: event.target.value });
};
render() {
const { classes } = this.props;
console.log(this.state);
return (
<form className={classes.container} noValidate>
<TextField
id="date"
label="Birthday"
type="date"
value={this.state.date}
onChange={this.handleChange}
className={classes.textField}
InputLabelProps={{
shrink: true
}}
/>
</form>
);
}
}
DatePickers.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(DatePickers);
I have created a user register page on the react-admin template, but when it's rendered, the sidebar and appbar are being rendered as well.
The default login page in the template gets rendered without these 2 components.
How do I configure the app so that the register page also gets rendered without the sidebar and appbar?
MyLayout.js
// in src/MyLayout.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withStyles, MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import {
AppBar,
Menu,
Notification,
Sidebar,
setSidebarVisibility,
} from 'react-admin';
const styles = theme => ({
root: {
display: 'flex',
flexDirection: 'column',
zIndex: 1,
minHeight: '100vh',
backgroundColor: theme.palette.background.default,
position: 'relative',
},
appFrame: {
display: 'flex',
flexDirection: 'column',
overflowX: 'auto',
},
contentWithSidebar: {
display: 'flex',
flexGrow: 1,
},
content: {
display: 'flex',
flexDirection: 'column',
flexGrow: 2,
padding: theme.spacing.unit * 3,
marginTop: '4em',
paddingLeft: 5,
},
});
class MyLayout extends Component {
componentWillMount() {
this.props.setSidebarVisibility(true);
}
render() {
const {
children,
classes,
dashboard,
isLoading,
logout,
open,
title,
} = this.props;
return (
<div className={classes.root}>
<div className={classes.appFrame}>
<AppBar title={title} open={open} logout={logout} color="primary"/>
<main className={classes.contentWithSidebar}>
<Sidebar>
<Menu logout={logout} hasDashboard={!!dashboard} />
</Sidebar>
<div className={classes.content}>
{children}
</div>
</main>
<Notification />
</div>
</div>
);
}
}
MyLayout.propTypes = {
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
dashboard: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string,
]),
isLoading: PropTypes.bool.isRequired,
logout: 'componentPropType',
setSidebarVisibility: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
};
const mapStateToProps = state => ({ isLoading: state.admin.loading > 0 });
export default connect(mapStateToProps, { setSidebarVisibility })(withStyles(styles)(MyLayout));
register.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { propTypes, reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import Card from '#material-ui/core/Card';
import CardActions from '#material-ui/core/CardActions';
import CircularProgress from '#material-ui/core/CircularProgress';
import TextField from '#material-ui/core/TextField';
import { withStyles } from '#material-ui/core/styles';
import LockIcon from '#material-ui/icons/Lock';
import { Notification, translate, userLogin } from 'react-admin';
import { Link } from 'react-router-dom';
import dataProvider from './dataProvider';
const styles = theme => ({
main: {
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'flex-start',
background: 'url(https://source.unsplash.com/random/1600x900)',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
},
card: {
minWidth: 300,
marginTop: '6em',
},
avatar: {
margin: '1em',
display: 'flex',
justifyContent: 'center',
},
icon: {
backgroundColor: theme.palette.secondary.main,
},
hint: {
marginTop: '1em',
display: 'flex',
justifyContent: 'center',
color: theme.palette.grey[500],
},
form: {
padding: '0 1em 1em 1em',
},
input: {
marginTop: '1em',
},
actions: {
padding: '0 1em 1em 1em',
},
});
// see http://redux-form.com/6.4.3/examples/material-ui/
const renderInput = ({
meta: { touched, error } = {},
input: { ...inputProps },
...props
}) => (
<TextField
error={!!(touched && error)}
helperText={touched && error}
{...inputProps}
{...props}
fullWidth
/>
);
class Register extends Component {
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
email: '',
password: '',
submitted: false
};
//this.handleSubmit = this.handleSubmit.bind(this);
this.handleFirstNameChange = this.handleFirstNameChange.bind(this);
this.handleLastNameChange = this.handleLastNameChange.bind(this);
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
}
handleFirstNameChange(e){
this.setState({firstName:e.target.value});
}
handleLastNameChange(e) {
this.setState({ lastName: e.target.value });
}
handleEmailChange(e) {
this.setState({ email: e.target.value });
}
handlePasswordChange(e) {
this.setState({ password: e.target.value });
}
register = () => {
this.setState({ submitted: true });
const { firstName, lastName, email, password } = this.state;
//console.log(data);
//console.log(firstName);
dataProvider('CREATE', 'user/register', { 'data': { 'fname': firstName, 'lname': lastName, 'email': email, 'password': password } });
/*dataProvider('CREATE', 'user/register', { data: {
fname: 1, lname: 5, email: test, password: 1234
} });*/
}
render() {
const { classes, handleSubmit, isLoading, translate } = this.props;
return (
<div className={classes.main}>
<Card className={classes.card}>
<div className={classes.avatar}>
<Avatar className={classes.icon}>
<LockIcon />
</Avatar>
</div>
<form onSubmit={handleSubmit(this.register)}>
<div className={classes.hint}></div>
<div className={classes.form}>
<div className={classes.input}>
<Field
name="firstName"
component={renderInput}
label={'First Name'}
disabled={isLoading}
value={this.state.firstName}
onChange={this.handleFirstNameChange}
/>
</div>
<div className={classes.input}>
<Field
name="lastName"
component={renderInput}
label={'Last Name'}
disabled={isLoading}
value={this.state.lastName}
onChange={this.handleLastNameChange}
/>
</div>
<div className={classes.input}>
<Field
name="email"
component={renderInput}
label={'Email'}
disabled={isLoading}
value={this.state.email}
onChange={this.handleEmailChange}
/>
</div>
<div className={classes.input}>
<Field
name="password"
component={renderInput}
label={translate('ra.auth.password')}
type="password"
disabled={isLoading}
value={this.state.password}
onChange={this.handlePasswordChange}
/>
</div>
</div>
<CardActions className={classes.actions}>
<Button
variant="raised"
type="submit"
color="primary"
disabled={isLoading}
className={classes.button}
fullWidth
>
{isLoading && (
<CircularProgress size={25} thickness={2} />
)}
{'Register'}
</Button>
</CardActions>
<CardActions className={classes.actions}>
<Button
variant="raised"
color="secondary"
disabled={isLoading}
className={classes.button}
fullWidth
>
<Link to={{pathname: "/login"}} style={{textDecoration: 'none', color:'#fff'}} >Sign In</Link>
</Button>
</CardActions>
</form>
</Card>
<Notification />
</div>
);
}
}
Register.propTypes = {
...propTypes,
authProvider: PropTypes.func,
classes: PropTypes.object,
previousRoute: PropTypes.string,
translate: PropTypes.func.isRequired,
userLogin: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({ isLoading: state.admin.loading > 0 });
const enhance = compose(
translate,
reduxForm({
form: 'signIn',
validate: (values, props) => {
const errors = {};
const { translate } = props;
if (!values.fname) {
errors.fname = translate('ra.validation.required');
}
if (!values.lname) {
errors.lname = translate('ra.validation.required');
}
if (!values.email) {
errors.email = translate('ra.validation.required');
}
if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email) ) {
errors.email = 'A valid email is required';
}
if (!values.password) {
errors.password = translate('ra.validation.required');
}
return errors;
},
}),
connect(
mapStateToProps,
{ userLogin }
),
withStyles(styles)
);
export default enhance(Register);
I believe you will use a customRoute to add your register page to router. You can add a 'noLayout' for not displaying appbar and sidebar
https://github.com/marmelab/react-admin/blob/master/docs/Admin.md#customroutes