React-Redux connecting dispatching a function component - reactjs

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

Related

I'm Getting Bad Request Error After Uploading the Video

I'm using Django and React to make a YouTube clone.
I tried to post the video on the frontend side, and got an error in terminal:
<QueryDict: {'title': ['Test Video Creation'], 'user': ['1'], 'description': ['Is it working?'], 'image': [<TemporaryUploadedFile: attachment.png (image/png)>], 'video': [<TemporaryUploadedFile: video-for-fatube.mp4 (video/mp4)>]}>Bad Request: /api/admin/create/
And the problem is not in the backend settings, because when I made a post request in the postman, the video was succesfully created.
creat.js File on frontend side to create videos.
import React, { useState } from 'react';
import axiosInstance from '../../axios';
import { useHistory } from 'react-router-dom';
//MaterialUI
import Avatar from '#material-ui/core/Avatar';
import axios from "axios";
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import TextField from '#material-ui/core/TextField';
import Grid from '#material-ui/core/Grid';
import Typography from '#material-ui/core/Typography';
import { makeStyles } from '#material-ui/core/styles';
import Container from '#material-ui/core/Container';
import IconButton from '#material-ui/core/IconButton';
import PhotoCamera from '#material-ui/icons/PhotoCamera';
const useStyles = makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(3),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
}));
export default function Create() {
const history = useHistory();
const initialFormData = Object.freeze({
title: '',
description: '',
});
const [videoData, updateFormData] = useState(initialFormData);
const [videoimage, setVideoImage] = useState(null);
const [videovideo, setVideoVideo] = useState(null);
const handleChange = (e) => {
if ([e.target.name] == 'image') {
setVideoImage({
image: e.target.files,
});
console.log(e.target.files);
}
if ([e.target.name] == 'video') {
setVideoVideo({
video: e.target.files,
});
console.log(e.target.files);
}
if ([e.target.name] == 'title') {
updateFormData({
...videoData,
[e.target.name]: e.target.value.trim(),
});
} else {
updateFormData({
...videoData,
[e.target.name]: e.target.value.trim(),
});
}
};
const handleSubmit = (e) => {
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
const URL = 'http://127.0.0.1:8000/api/admin/create/';
let formData = new FormData();
formData.append('title', videoData.title);
formData.append('user', 1);
formData.append('description', videoData.description);
formData.append('image', videoimage.image[0]);
formData.append('video', videovideo.video[0]);
axios
.post(URL, formData, config)
.then((res) => {
console.log(res.data);
})
.catch((err) => console.log(err));
};
const classes = useStyles();
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}></Avatar>
<Typography component="h1" variant="h5">
Create New Video
</Typography>
<form className={classes.form} noValidate>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="title"
label="Video Title"
name="title"
autoComplete="title"
onChange={handleChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="description"
label="Video Description"
name="description"
autoComplete="description"
onChange={handleChange}
multiline
rows={4}
/>
</Grid>
<input
accept="image/*"
className={classes.input}
id="video-image"
onChange={handleChange}
name="image"
type="file"
/>
<input
accept="video/*"
className={classes.input}
id="video-video"
onChange={handleChange}
name="video"
type="file"
/>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={handleSubmit}
>
Create Video
</Button>
</form>
</div>
</Container>
);
}
If you want to look at the whole project, here is Github: https://github.com/PHILLyaHI/diplom-work
What I did to make it work is I deleted files in the media folder and deleted all the videos.

Problem autoFocus Autocomplete Material UI

I am not able to focus on the first element of my form which is an Autocomplete component of the Material UI.
When opening the modal, the focus is on the 'unit' element, as shown in the image below:
It dont's work
I want the focus to be on the 'group' element, as shown below:
It work
Here is the Sandbox link:
https://codesandbox.io/s/awesome-bassi-zjp50?file=/src/MyForm.jsx:1422-1431
can anyone help me?
MyForm
import React from "react";
import { Formik, Form, Field } from "formik";
import * as Yup from "yup";
import { Grid } from "#material-ui/core";
import Input from "./components/Input";
import Autocomplete from "./components/Autocomplete";
const initialValues = {
id: 0,
group: {
value: 0,
label: ""
},
name: "",
unit: {
value: 0,
label: ""
}
};
const groupList = [
{
value: "1",
label: "Seeds"
},
{
value: "2",
label: "Fertilizers"
}
];
const unitList = [
{
value: "1",
label: "kg"
},
{
value: "2",
label: "t"
}
];
const validationSchema = Yup.object({
group: Yup.object().shape({
value: Yup.number().required().min(1).nullable()
}),
name: Yup.string().required().max(50),
unit: Yup.object().shape({
value: Yup.number().required().min(1).nullable()
})
});
export default function MyForm() {
function _onSubmit(fields, { props, setErrors, setSubmitting }) {
console.log(fields);
}
return (
<Formik
enableReinitialize={true}
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={_onSubmit}
>
{function Render({ errors, touched, isSubmitting, setFieldValue }) {
return (
<Form id="form">
<Grid container direction="row">
<Grid item xs={12}>
<Field
name="group"
autoFocus
component={Autocomplete}
label="Group"
options={groupList}
disabled={isSubmitting}
textFieldProps={{
fullWidth: true,
margin: "dense",
variant: "outlined"
}}
/>
</Grid>
<Grid item xs={12}>
<Field
name="name"
fullWidth
label="Name"
disabled={isSubmitting}
component={Input}
/>
</Grid>
<Grid item xs={12}>
<Field
name="unit"
component={Autocomplete}
label="Unit"
options={unitList}
disabled={isSubmitting}
textFieldProps={{
fullWidth: true,
margin: "dense",
variant: "outlined"
}}
/>
</Grid>
</Grid>
</Form>
);
}}
</Formik>
);
}
Popup Modal
import React from "react";
import {
Dialog,
DialogTitle,
DialogContent,
makeStyles,
Typography,
Divider,
DialogActions,
Button,
Grid,
Box
} from "#material-ui/core";
const useStyles = makeStyles((theme) => ({
dialogWrapper: {
padding: theme.spacing(2),
position: "absolute",
top: theme.spacing(5)
},
dialogTitle: {
paddingRight: 0
},
button: {
margin: theme.spacing(0.5),
textTransform: "none"
},
buttonsContainer: {
display: "flex",
flex: "0 0 auto",
justifyContent: "flex-end",
width: "100%",
height: "100%"
},
buttons: {
display: "flex"
},
loadingIcon: {
display: "flex",
alignItems: "center",
justifyContent: "center",
marginRight: theme.spacing(1)
}
}));
function Popup(props) {
const classes = useStyles();
const { title, children, openPopup, setOpenPopup, ...rest } = props;
return (
<Dialog
{...rest}
open={openPopup}
maxWidth="sm"
classes={{ paper: classes.dialogWrapper }}
>
<DialogTitle dividers="true" className={classes.dialogTitle}>
<div style={{ display: "flex" }}>
<Typography variant="h4" component="div" style={{ flexGrow: 1 }}>
{title}
</Typography>
</div>
</DialogTitle>
<Divider />
<DialogContent {...rest} direction="row">
{children}
</DialogContent>
<DialogActions {...rest}>
<Grid {...rest} container className={classes.buttonsContainer}>
<Box {...rest} className={classes.buttons}>
<Button
className={classes.button}
variant="contained"
color="primary"
type="submit"
form="form"
{...rest}
>
Save
</Button>
<Button
className={classes.button}
color="default"
fullWidth
onClick={() => {
setOpenPopup(false);
}}
{...rest}
>
Cancel
</Button>
</Box>
</Grid>
</DialogActions>
</Dialog>
);
}
export default Popup;
Autocomplete
import React from "react";
import { FieldProps, getIn } from "formik";
import { TextField, CircularProgress } from "#material-ui/core";
import MuiAutocomplete, {
createFilterOptions
} from "#material-ui/lab/Autocomplete";
const NewAutocomplete: React.FC<
FieldProps & {
label?: string,
options: Array<{ label: string, value: number }>
}
> = ({ textFieldProps, field, form, label, options, isLoading, ...props }) => {
const filterOptions = createFilterOptions({
matchFrom: "start",
limit: 500
});
const errorText =
getIn(form.touched, field.name) && getIn(form.errors, field.name);
const valueInit = [
{
value: 0,
label: ""
}
];
return (
<MuiAutocomplete
{...props}
{...field}
filterOptions={filterOptions}
options={[...valueInit, ...options]}
getOptionLabel={(option) => (option ? option.label : "")}
getOptionSelected={(option, value) => option.value === value?.value}
style={{ width: 300 }}
loading={isLoading}
value={field.value}
onChange={(e, value) => {
form.setFieldValue(field.name, value);
}}
renderInput={(props) => (
<>
<TextField
{...props}
{...textFieldProps}
label={label}
helperText={errorText?.value || errorText}
error={!!errorText}
autoFocus
InputProps={{
...props.InputProps,
endAdornment: (
<React.Fragment>
{isLoading ? (
<CircularProgress color="primary" size={20} />
) : null}
{props.InputProps.endAdornment}
</React.Fragment>
)
}}
/>
</>
)}
/>
);
};
export default NewAutocomplete;
Thanks for your help!
You're actually setting autoFocus to true on TextField by default inside your NewAutocomplete definition (src/components/Autocomplete.jsx:50 in your sandbox).
Once that line removed, you can then add autoFocus: true to textFieldProps value of your first Field in MyForm component.
See codesandbox example

Type '{}' is not assignable to type 'IProps'

I am using react with formik and typescript in my project. I am using withFormik HOC in my forms, code is below
import React from "react";
//Libraries import........
import {
Container,
Box,
Link,
Grid,
Checkbox,
FormControlLabel,
TextField,
CssBaseline,
Avatar,
Typography,
makeStyles,
Theme,
} from "#material-ui/core";
import LockOutlinedIcon from "#material-ui/icons/LockOutlined";
import { Form, FormikProps, FormikValues, useFormik, withFormik } from "formik";
import * as Yup from "yup";
//Components import........
import { Button } from "#components/button";
//Interfaces ..........
interface FormValues {
email?:string,
password?:string
}
interface IProps extends FormikProps<FormValues> {
// onSubmit(e:React.FormEvent<HTMLInputElement>):void
}
const useStyles = makeStyles((theme: Theme) => ({
paper: {
marginTop: theme.spacing(8),
display: "flex",
flexDirection: "column",
alignItems: "center",
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
}));
const Login: React.FC<IProps> = (props) => {
const classes = useStyles();
const { handleSubmit, handleBlur, handleChange, handleReset, errors } = props;
console.log(errors);
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoFocus
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
<Grid container>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link href="#" variant="body2">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</form>
</div>
<Box mt={8}></Box>
</Container>
);
};
export const LoginView = withFormik<IProps, FormValues>({
enableReinitialize: true,
mapPropsToValues: (props) => {
console.log(props);
return { email: "", password: "" };
},
validationSchema: Yup.object({
// email: Yup.string().required(),
}),
handleSubmit: (values) => {
console.log(values);
},
})(Login);
and then when in my project i import and use LoginView inside my pages like
import React from "react";
import { LoginView } from "#components/user/login";
const Home: React.FC = (props: any) => {
return (
<div
style={{ display: "flex", flexDirection: "column", minHeight: "100vh" }}
>
<LoginView />
</div>
);
};
export default Home;
i get an error where i write
Type '{}' is not assignable to type 'IProps'. Type '{}' is missing
the following properties from type 'FormikState': values,
errors, touched, isSubmitting, and 2 more.
First Generic Type provided to withFormik shouldn't extend from FormikValues.
Define IProps like this
interface IProps {
// onSubmit(e:React.FormEvent<HTMLInputElement>):void
}
See this https://codesandbox.io/s/stackoverflow-69143042-wlvz9

How render a list of options with renderOption in material UI

I want to change the background colour of the options inside an Autocomplete component, and the closest I can get is by using the renderOption prop.
The problem is that I can't figure out how to iterate (using map()) the options that I have in my state.
What I would like to do is something like
{state.myOptions.map( option => {
// here I would like to call renderOption = .....
}
Inside the <Autocomplete/> component
Is it possible to implement something like this or is there a well defined manner to do it?
EDIT
This is the component
import React, { useEffect } from 'react'
import { useForm, Form } from './hooks/useForm'
import EventIcon from '#material-ui/icons/Event';
import { makeStyles, TextField, Typography } from '#material-ui/core'
import CustomTextField from './inputs/CustomTextField';
import { Autocomplete } from '#material-ui/lab';
import { connect } from 'react-redux'
const EventForm = (props) => {
// Redux
const { family } = props
// React
const initialState = {
email: "",
password: "",
errors: {
email: "",
password: ""
},
familyMembers: ["rgeg"]
}
const { state, handleOnChange, setState } = useForm(initialState)
useEffect(() => {
family && state.familyMembers !== family.members && setState({
...state,
familyMembers: family.members
})
})
// Material UI
const useStyles = makeStyles(theme => (
{
message: {
marginTop: theme.spacing(3)
},
icon: {
backgroundColor: "lightgrey",
padding: "10px",
borderRadius: "50px",
border: "2px solid #3F51B5",
marginBottom: theme.spacing(1)
},
typography: {
marginBottom: theme.spacing(1),
marginTop: theme.spacing(4)
},
customTextField: {
marginTop: theme.spacing(0)
},
dateTimeWrapper: {
marginTop: theme.spacing(4)
}
}
))
const classes = useStyles()
return (
<>
<div>WORK IN PROGRESS...</div>
<br />
<br />
<EventIcon className={classes.icon} />
<Form
title="Add new event"
>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a title for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Title"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a location for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Location"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Which member/s of the family is/are attending
</Typography>
<Autocomplete
multiple
id="tags-outlined"
options={state.familyMembers}
getOptionLabel={(option) => option.name}
// defaultValue={[familyMembers[0]]}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Members Attending"
placeholder="Family Member"
/>
)}
/>
</Form>
</>
);
}
// Redux
const mapStateToProps = (state) => {
return {
family: state.auth.family
}
}
export default connect(mapStateToProps)(EventForm);
If you only want to override the color of the option you can do it by overriding it's styles. No need to make custom option rendering function.
Above is the example of how can you achieve that.
import React, { useEffect } from 'react'
import { useForm, Form } from './hooks/useForm'
import EventIcon from '#material-ui/icons/Event';
import { makeStyles, TextField, Typography } from '#material-ui/core'
import CustomTextField from './inputs/CustomTextField';
import { Autocomplete } from '#material-ui/lab';
import { connect } from 'react-redux'
const EventForm = (props) => {
// Redux
const { family } = props
// React
const initialState = {
email: "",
password: "",
errors: {
email: "",
password: ""
},
familyMembers: ["rgeg"]
}
const { state, handleOnChange, setState } = useForm(initialState)
useEffect(() => {
family && state.familyMembers !== family.members && setState({
...state,
familyMembers: family.members
})
})
// Material UI
const useStyles = makeStyles(theme => (
{
message: {
marginTop: theme.spacing(3)
},
icon: {
backgroundColor: "lightgrey",
padding: "10px",
borderRadius: "50px",
border: "2px solid #3F51B5",
marginBottom: theme.spacing(1)
},
typography: {
marginBottom: theme.spacing(1),
marginTop: theme.spacing(4)
},
customTextField: {
marginTop: theme.spacing(0)
},
dateTimeWrapper: {
marginTop: theme.spacing(4)
},
option: {
backgroundColor: 'red'
}
}
))
const classes = useStyles()
return (
<>
<div>WORK IN PROGRESS...</div>
<br />
<br />
<EventIcon className={classes.icon} />
<Form
title="Add new event"
>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a title for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Title"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a location for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Location"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Which member/s of the family is/are attending
</Typography>
<Autocomplete
multiple
id="tags-outlined"
classes={{
option: classes.option
}}
options={state.familyMembers}
getOptionLabel={(option) => option.name}
// defaultValue={[familyMembers[0]]}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Members Attending"
placeholder="Family Member"
/>
)}
/>
</Form>
</>
);
}
// Redux
const mapStateToProps = (state) => {
return {
family: state.auth.family
}
}
export default connect(mapStateToProps)(EventForm);
Wow, this took a while but the solution seems to use 'renderTags' algong with
here is the exact solution
import React, { useEffect } from 'react'
import { useForm, Form } from './hooks/useForm'
import EventIcon from '#material-ui/icons/Event';
import { makeStyles, TextField, Typography } from '#material-ui/core'
import CustomTextField from './inputs/CustomTextField';
import { Autocomplete } from '#material-ui/lab';
import { connect } from 'react-redux'
import Chip from '#material-ui/core/Chip';
import getColorvalue from './outputs/ColorValues'
const EventForm = (props) => {
// Redux
const { family } = props
// React
const initialState = {
email: "",
password: "",
errors: {
email: "",
password: ""
},
familyMembers: ["rgeg"]
}
const { state, handleOnChange, setState } = useForm(initialState)
useEffect(() => {
family && state.familyMembers !== family.members && setState({
...state,
familyMembers: family.members
})
})
// Material UI
const useStyles = makeStyles(theme => (
{
message: {
marginTop: theme.spacing(3)
},
icon: {
backgroundColor: "lightgrey",
padding: "10px",
borderRadius: "50px",
border: "2px solid #3F51B5",
marginBottom: theme.spacing(1)
},
typography: {
marginBottom: theme.spacing(1),
marginTop: theme.spacing(4)
},
customTextField: {
marginTop: theme.spacing(0)
},
dateTimeWrapper: {
marginTop: theme.spacing(4)
}
}
))
const classes = useStyles()
return (
<>
<div>WORK IN PROGRESS...</div>
<br />
<br />
<EventIcon className={classes.icon} />
<Form
title="Add new event"
>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a title for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Title"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a location for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Location"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Which member/s of the family is/are attending
</Typography>
<Autocomplete
multiple
id="tags-outlined"
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="outlined"
key={option}
style={{
backgroundColor: `${getColorvalue(state.familyMembers[state.familyMembers.indexOf(option)].color)}`,
color: "white"
}}
label={option.name}
onDelete={() => console.log("test")}
{...getTagProps({ index })}
/>
))
}
options={state.familyMembers}
getOptionLabel={(option) => option.name}
// defaultValue={[familyMembers[0]]}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Members Attending"
placeholder="Family Member"
/>
)}
/>
</Form>
</>
);
}
// Redux
const mapStateToProps = (state) => {
return {
family: state.auth.family
}
}
export default connect(mapStateToProps)(EventForm);

Login mutation not working consistently in material-ui.

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)

Resources