Trying to make a POST request to Firebase Firestore - reactjs

import axios from 'axios'
import Copyright from '../components/Copyright'
// Material UI Stuff
import { makeStyles } from '#material-ui/core/styles'
import CircularProgress from '#material-ui/core/CircularProgress'
import Container from '#material-ui/core/Container'
import Typography from '#material-ui/core/Typography'
import TextField from '#material-ui/core/TextField'
import Button from '#material-ui/core/Button'
import Box from '#material-ui/core/Box'
const INITIAL_STATE = {
cohort: '',
program: '',
github: '',
website: '',
linkedIn: ''
}
const inputProps = {
step: 1,
min: 1,
max: 99
}
const Profile = () => {
const [formData, setFormData] = useState(INITIAL_STATE)
const [isloading, setIsLoading] = useState(false)
const [errors, setErrors] = useState({})
const [user, setUser] = useState(null)
const isInvalid = !formData.cohort || !formData.program || isloading
const handleInputChange = field => e => {
setFormData({ ...formData, [field]: e.target.value })
}
const fetchProfile = async () => {
const token = await localStorage.FBIdToken
await axios
.get(`/user`, {
headers: {
Authorization: `${token}`
}
})
.then(res => {
setUser(res.data)
setFormData({
...formData,
github: res.data.user.github ? res.data.user.github : '',
website: res.data.user.website ? res.data.user.website : '',
linkedIn: res.data.user.linkedIn ? res.data.user.linkedIn : '',
cohort: res.data.user.cohort,
program: res.data.user.program
})
})
.catch(err => console.log('*** WRONG ***'))
}
useEffect(() => {
fetchProfile()
}, [])
const handleSubmit = async e => {
e.preventDefault()
setIsLoading(true)
const fireToken = await localStorage.FBIdToken
await axios
.post(
(`/user`,
{
headers: {
Authorization: `${fireToken}`
}
})
)
.then(res => {
console.log(res.data)
setIsLoading(false)
})
.catch(err => {
console.log(err)
setIsLoading(false)
})
}
const useStyles = makeStyles(theme => ({
form: {
textAlign: 'center',
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
position: 'relative'
},
logo: {
width: 180,
height: 180,
display: 'block',
alignItems: 'center'
},
submit: {
margin: theme.spacing(3, 0, 2),
position: 'relative'
},
progress: {
position: 'absolute'
},
customError: {
color: 'red',
fontSize: '0.8rem',
width: '100%',
position: 'absolute'
}
}))
const classes = useStyles()
return (
<>
{user ? (
<div>
<Container component="main" maxWidth="xs">
<div className={classes.paper}>
<img
src={user.user.imageUrl}
alt="wyncode logo"
className={classes.logo}
/>
<Typography variant="h3" className={classes.pageTitle}>
Sign Up
</Typography>
<form noValidate onSubmit={handleSubmit} className={classes.form}>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="website"
type="website"
label="Website"
name="website"
autoComplete="website"
value={formData.website}
onChange={handleInputChange('website')}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="cohort"
type="number"
inputProps={inputProps}
label="Cohort #"
name="cohort"
autoComplete="cohort"
helperText={errors.cohort}
error={errors.cohort ? true : false}
value={formData.cohort}
onChange={handleInputChange('cohort')}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="linkedIn"
type="linkedIn"
label="LinkedIn"
name="linkedIn"
autoComplete="linkedIn"
value={formData.linkedIn}
onChange={handleInputChange('linkedIn')}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="github"
type="github"
label="GitHub"
name="github"
autoComplete="github"
value={formData.github}
onChange={handleInputChange('github')}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="program"
type="program"
label="Program"
name="program"
helperText={errors.program}
error={errors.program ? true : false}
autoComplete="program"
value={formData.program}
onChange={handleInputChange('program')}
/>
{errors.general && (
<Typography variant="body2" className={classes.customError}>
{errors.general}
</Typography>
)}
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
disabled={isInvalid}
>
Update User Info
{isloading && (
<CircularProgress size={30} className={classes.progress} />
)}
</Button>
</form>
</div>
<Box mt={8}>
<Copyright />
</Box>
</Container>
</div>
) : (
<CircularProgress size={60} className={classes.progressOne} />
)}
</>
)
}
export default Profile
I am fetching the token for my headers from local storage and trying to post the form data to my user endpoint. When I post, I get an error stating:
xhr.js:178 POST http://localhost:3000/[object%20Object] 404 (Not
Found)
I'm hitting the correct end point, but for some reason the post isn't hitting the /user endpoint with my form data. any insight would be awesome.

Related

can't edit data in Textfield in reactjs

I'm trying to edit the api data on a Mui datagrid. I want a modal form to pop on clicking the edit icon. Everything works fine, I'm getting the data to be edited and all that. thing is the data I'm getting in textfield for edit, is not changing the value. it's not taking any inputs I'm giving, it stays the same. what should I do to make the textfields editable ? Thanks in advance.
export default function ContactsCard(props) {
const [ContactData, setContactData] = useState([]);
const [open, setOpen] = useState(false);
const [formInputData, setformInputData] = useState(
{
name: '',
details: '',
}
);
const handleChange = (evnt) => {
const newInput = (data) => ({
...data,
[evnt.target.name]: evnt.target.value
});
setformInputData(newInput);
}
const showData = () => {
axios.get('http://localhost:8006/api/v2/get/beneficiaries-list').then(function (res) {
try {
var result = res.data;
// console.log(result.data)
setContactData(result.data)
}
catch (error) {
console.log(error)
}
})
}
const handleSubmit = (evnt) => {
evnt.preventDefault();
const formData = new FormData(); //formdata object
formData.append('nickname', formInputData.name); //append the values with key, value pair
formData.append('target', formInputData.details);
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
axios.post('http://localhost:8006/api/v2/save/beneficiary', formData, config)
.then(response => {
if (response.data.success === true) {
showData()
alert(response.data.message)
}
})
.catch(error => {
alert(error.message);
});
setformInputData({ name: "", details: "" });
setOpen(false);
}
const handleEdit = (id) => {
var edit_id = id.row.id
console.log(edit_id)
setOpen(true)
setformInputData(ContactData.find((data) => data.id === edit_id))
setOpen(true)
}
const handleOpen = () => setOpen(true)
const handleClose = () => setOpen(false);
useEffect(() => {
showData();
}, [])
const handleDelete = (id) => {
var del_id = id.row.id
console.log(del_id)
axios.post('http://localhost:8006/api/v2/remove/beneficiary/', { id: del_id }).then(res => {
console.log(res.data)
if (res.data.success === true) {
alert(res.data.message)
showData();
console.log('ajh');
}
})
};
const columns = [
{ field: 'name', headerName: 'Nick Name', width: '130' },
{ field: 'details', headerName: 'Deposit address', width: '130' },
{
field: 'actions',
type: 'actions',
width: '130',
headerName: 'Actions',
cellClassName: 'actions',
renderCell: (id) => {
return (
<>
<IconButton color="primary" onClick={(e) => { handleEdit(id) }}>
<EditIcon />
</IconButton>
<IconButton color="error" onClick={(e) => {
handleDelete(id)
}}>
<DeleteIcon />
</IconButton>
</>
);
},
},
];
return (
<Card {...props}>
<CardContent>
<ContactDataGrid rows={ContactData} columns={columns} />
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<FormControl sx={style} mt={1} >
<Typography sx={{ fontSize: 16, fontWeight: 'bold' }} color="text.secondary" gutterBottom>
Edit Contact
</Typography>
<Stack flexDirection='column' gap={1.5} mt={1}>
<TextField autoFocus required
id="filled-hidden-label-small"
label="Nick Name" variant="outlined" size="small"
onChange={handleChange}
value={formInputData.name}
name="Nick Name"
className="form-control"
/>
<TextField required
id="filled-hidden-label-small"
label="Deposit Address" variant="outlined" size="small"
onChange={handleChange}
value={formInputData.details}
name="Amount"
className="form-control"
/>
</Stack>
<Stack flexDirection='row' justifyContent={'center'} gap={1.5} mt={1}>
<Button
variant="contained"
type="submit" color="error"
onClick={handleClose}
sx={{ alignSelf: 'center' }} >Cancel</Button>
<Button
variant="contained"
type="submit"
onClick={handleSubmit}
sx={{ alignSelf: 'center', backgroundColor: '#000073' }} >Submit</Button>
</Stack>
</FormControl>
</Fade>
</Modal>
</CardContent>
</Card>
);
}
Your handleChange function is updating the new value in event.target.name but, the name doesn't match with the value. You can try changing the name to match the value getter:
<TextField autoFocus required
id="filled-hidden-label-small"
label="Nick Name" variant="outlined" size="small"
onChange={handleChange}
value={formInputData.name}
name="name" // this changed
className="form-control"
/>
<TextField required
id="filled-hidden-label-small"
label="Deposit Address" variant="outlined" size="small"
onChange={handleChange}
value={formInputData.details}
name="details" // this changed
className="form-control"
/>

How can we validate our form using formik

I am unable to validate the following form because of checkbox handling. I have to implement this type of thing in many of my form issue is same i am unable to handle the on change on some of that form to handle the checkbox.
import React, { useEffect } from "react";
import {
Grid,
Typography,
Button,
Box,
TextField,
FormControlLabel,
} from "#mui/material";
import { Formik, Form, Field, ErrorMessage } from "formik";
import axios from "axios";
import * as Yup from "yup";
import API from "../../../../../E2E/Axios.Utils";
const AddReport = ({
handleDialog,
update,
setUpdate,
setAlert,
setAlertContent,
setAlertType,
}) => {
const [tabData, setTabData] = React.useState([]);
const token = localStorage.getItem("token").toString();
const onSubmit = (values, props) => {
console.log("values", values);
const tabs = [];
values.tab_id.map((ele) => {
if (typeof ele === "string") {
tabs.push(Number(ele));
}
console.log("tabs", tabs);
return tabs;
});
const data = {
report_type_name: values.report_type_name,
description: values.description,
tab_id: tabs,
remember: false,
};
API.post("/app/secure/report_type/", data, {
headers: { Authorization: `Bearer ${token}` },
})
.then((res) => {
handleDialog();
setUpdate(update + 1);
setAlertContent(res.data.msg);
setAlertType("success");
setAlert(true);
})
.catch((err) => {
console.log(err);
if (err?.response?.status == 401) {
setAlertContent("Token is invalid or expired.");
setAlertType("error");
setAlert(true);
} else {
setAlertContent(`Error : Something went wrong.`);
setAlertType("error");
setAlert(true);
}
});
props.resetForm();
};
This API is used to get the tab details
const getTabData = () => {
API.get("/app/secure/my_tab/", {
headers: { Authorization: `Bearer ${token}` },
})
.then((res) => {
console.log("GetData", res);
setTabData(res.data.tab_list);
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
getTabData();
}, []);
const initialValues = {
report_type_name: "",
description: "",
tab_id: [],
remember: false,
};
const AlphaNumeric = /^[a-zA-Z0-9](([_ -])?[a-zA-Z0-9]*)+$/;
const validationSchema = Yup.object().shape({
report_type_name: Yup.string()
.matches(AlphaNumeric, "Please enter valid report type name.")
.max(40, "Tab Name must be at most 40 characters.")
.required("This field is required."),
description: Yup.string()
.matches(AlphaNumeric, "Please enter valid description.")
.max(160, "Description must be at most 160 characters.")
.required("This field is required."),
});
return (
<Box>
<Typography
id="modal-modal-title"
variant="h6"
component="h2"
sx={{ marginBottom: "4%" }}
>
Add Report
</Typography>
<Box>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validationSchema={validationSchema}
>
{(props) => (
<Form noValidate>
<Field
as={TextField}
name="report_type_name"
label="Name"
placeholder="Name"
fullWidth
error={
props.errors.report_type_name &&
props.touched.report_type_name
}
size="small"
helperText={<ErrorMessage name="report_type_name" />}
required
sx={{ marginBottom: "4%" }}
/>
<Field
as={TextField}
name="description"
label="Description"
placeholder="Description"
fullWidth
error={props.errors.description && props.touched.description}
size="small"
helperText={<ErrorMessage name="description" />}
required
sx={{ marginBottom: "4%" }}
/>
<Typography variant="subtitle1" sx={{ textAlign: "left" }}>
<span style={{ fontWeight: "bold" }}>Tabs</span>
</Typography>
<Grid container>
{tabData.map((item) => {
return (
<Grid
item
xl={4}
lg={4}
md={4}
sm={12}
xs={12}
key={item.tab_id}
style={{
display: "flex",
width: "150px",
}}
>
<FormControlLabel
control={
<Field
name="tab_id"
label={item.tab_name}
value={item.tab_id.toString()}
style={{ margin: "8px" }}
type="checkbox"
/>
}
/>
<div style={{ margin: "8px" }}>{item.tab_name}</div>
</Grid>
);
})}
</Grid>
<Grid
container
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: "1rem",
mt: 2,
}}
>
<Grid item>
<Button onClick={handleDialog}>Disagree</Button>
</Grid>
<Grid item>
<Button
type="submit"
variant="contained"
sx={{
padding: "10px",
borderRadius: "20px",
width: "101px",
height: "36px",
}}
>
Save
</Button>
</Grid>
</Grid>
</Form>
)}
</Formik>
</Box>
</Box>
);
};
export default AddReport;
I have put all the code in this. facing same issue.
I dont know from where the error is coming.

React: The image that the user selected is different from the result coming from res.data

I'm creating a component that will create a post with descriptions and images, using react js and multer. The problem is the some of the images that I selected have been remove and replacing twice by the same images. I already check the code by using console log the result is fine but after the axios post request the list of images changes. Can you help me why this thing happen?
createPost.js
const UserPost = ({ userData }) => {
const { username: usernameProps } = userData;
const [postInfo, setPostInfo] = useState({
username: '',
description: '',
postImageCollection: '',
});
useEffect(() => {
setPostInfo({ username: usernameProps });
}, [usernameProps]);
const dispatch = useDispatch();
const classes = UseStyles();
const handlePost = async (e) => {
e.preventDefault();
const fData = new FormData();
for (let i = 0; i < postImageCollection.length; i++) {
fData.append('postImageCollection', postImageCollection[i]);
**console.log('postImageCollection', postImageCollection[i]); <-- here the list of images is correct**
}
try {
await axios({
method: 'POST',
url: 'http://localhost:5000/user/posts',
data: fData,
headers: {
'content-type': 'multipart/form-data',
},
}).then((res) => {
**console.log(res.data); <-- this is where the list of images changes**
// setPostInfo((prevImgCollection) => ({
// ...prevImgCollection,
// postImageCollection: res.data,
// }));
});
} catch (error) {
console.log(error.response);
}
dispatch(createPost(postInfo));
};
const handleChangeImageCollection = (e) => {
const newArray = [...postImageCollection];
for (let i = 0; i < e.target.files.length; i++) {
newArray.push(e.target.files[i]);
}
setPostImageCollection(newArray);
if (newArray.length > 5) {
setCurrentImageHide('+' + (newArray.length - imagePerPost));
}
};
const handleChange = (e) => {
const { name, value } = e.target;
setPostInfo((prev) => ({ ...prev, [name]: value }));
};
const indexOfLastImage = currentImagePost * imagePerPost;
const indexOfFirstImg = indexOfLastImage - imagePerPost;
const currentImage = postImageCollection.slice(
indexOfFirstImg,
indexOfLastImage
);
return (
<Container
component='div'
className={classes.userPostContainer}
style={{ padding: '0', margin: '0' }}
>
<Card className={classes.userPostCard} style={{ boxShadow: 'none' }}>
<CardContent style={{ padding: '0 0 1.5em 0' }}>
<form noValidate onSubmit={handlePost}>
<Grid container>
<Grid item xs={12}>
<TextField
variant='outlined'
id='description'
name='description'
type='text'
value={postInfo.description}
onChange={handleChange}
placeholder={`What's on your mind, ${
userData && userData.username
}`}
fullWidth
InputLabelProps={{
classes: {
root: classes.label,
focused: classes.focused,
},
}}
InputProps={{
className: classes.textfieldBg,
classes: {
root: classes.cssOutlinedInputBg,
focused: classes.cssFocused,
notchedOutline: classes.NonotchedOutline,
},
}}
/>
</Grid>
<Grid
xs={12}
container
className={classes.postPhotoContainer}
style={{ padding: '0' }}
>
<Grid xs={12}>
<ImageList
postImageCollection={currentImage}
currentImageHide={currentImageHide}
/>
</Grid>
<Grid xs={10}>
<input
accept='image/*'
id='postImageCollection-file-button'
type='file'
name='postImageCollection'
onChange={handleChangeImageCollection}
multiple
/>
<label htmlFor='postImageCollection-file-button'>
<ImageIcon />
Add Photo
</label>
</Grid>
<Grid
xs={2}
style={{
padding: '0',
margin: '0',
display: 'flex',
justifyContent: 'center',
}}
>
<Button
type='submit'
variant='contained'
startIcon={<SendIcon />}
>
Post
</Button>
</Grid>
</Grid>
</Grid>
</form>
</CardContent>
</Card>
</Container>
);
};
The correct output
0: "http://localhost:5000/public/images/post/postImageCollection-1616065863529.jpg"
1: "http://localhost:5000/public/images/post/postImageCollection-1616065863529.jpg"
2: "http://localhost:5000/public/images/post/postImageCollection-1616065863530.jpg"
The incorrect output
0: "http://localhost:5000/public/images/post/postImageCollection-1616065863529.jpg"
1: "http://localhost:5000/public/images/post/postImageCollection-1616065863529.jpg"
2: "http://localhost:5000/public/images/post/postImageCollection-1616065863530.jpg"

How to get this Formik logic to work like I want the submit Button to do what I want

I learn Reactjs and have this Sign in Component that uses Formik
My problem is that Facebook, Google, and Twitter work as expected but I can't get password Sign-in to work.
When I click the Button the onSubmit={(values, { setSubmitting }) => {... is called and the values contain the email and password but I want the Button to use the same logic as Facebook, Google and Twitter Button's`, if that's possible.
You see when clicking Facebook, Google, and Twitter they all onClick={this.submitFormType... that sets the state to the respective provider. Then it calls the Formik handleSubmit.
This is what I want for the submit Button also or maybe I should rethink my design not using Formik.
import React from 'react';
import { connect } from 'react-redux';
import { compose } from 'recompose';
import { Link as RouterLink } from 'react-router-dom';
import * as Yup from 'yup';
import { Formik } from 'formik';
import { Box, Button, Container, Grid, Link, TextField, Typography } from '#material-ui/core';
import { withStyles } from '#material-ui/core/styles';
import Dots from 'react-activity/lib/Dots';
import { withFirebase } from '../../../../firebase';
import FacebookIcon from '../../../../assets/Facebook';
import GoogleIcon from '../../../../assets/Google';
import TwitterIcon from '../../../../assets/twitter-logo';
import Page from '../../utils/Page';
import MotionDiv from '../../utils/MotionDiv';
import { withEmailVerification, withAuthorization, AuthUserContext } from '../../../../session';
import { changeToUserRole } from '../../../../redux/userData/user.actions';
import * as ROLES from '../../../../constants/roles';
import 'react-activity/lib/Dots/Dots.css';
import * as SIGN_IN from '../../../../constants/signinmethods';
const useStyles = theme => ({
root: {
backgroundColor: theme.palette.primary.light,
minHeight: '100vh',
paddingBottom: theme.spacing(3),
paddingTop: theme.spacing(3),
},
container: {
backgroundColor: theme.palette.primary.main,
paddingTop: theme.spacing(3),
},
facebook: {
backgroundColor: '#3b5999',
color: 'white',
'&:hover': {
backgroundColor: '#4d70ba',
},
},
google: {
backgroundColor: '#ffffff',
textColor: 'black',
},
twitter: {
backgroundColor: '#ffffff',
textColor: 'black',
},
button: {
backgroundColor: theme.palette.primary.main,
boxShadow: theme.shadows[5],
},
});
const INITIAL_EMAIL_STATE = {
email: '',
password: '',
error: null,
};
class LoginManagementBase extends React.Component {
constructor() {
super();
this.state = {
activeSignInMethods: [],
anonymousSignIn: null,
error: null,
formType: null,
...INITIAL_EMAIL_STATE,
};
this.submitFormType = this.submitFormType.bind(this);
this.facebookSignIn = this.facebookSignIn.bind(this);
this.googleSignIn = this.googleSignIn.bind(this);
this.twitteSignIn = this.twitteSignIn.bind(this);
}
componentDidMount() {
this.fetchSignInMethods();
}
fetchSignInMethods = () => {
const { firebase, authUser } = this.props;
const email = authUser.email === null ? 'none#guest.ac' : authUser.email;
firebase.auth
.fetchSignInMethodsForEmail(email)
.then(activeSignInMethods =>
this.setState({
activeSignInMethods,
anonymousSignIn: activeSignInMethods.length === 0,
error: null,
}),
)
.catch(error => this.setState({ error }));
};
onSocialLoginLink = provider => {
// Do stuf to sign in..............
};
// this.setState({ count: this.state.count + 1 })
submitFormType = (formTypeSubmited, handleSumitType) => () => {
this.setState({
formType: formTypeSubmited,
});
handleSumitType();
};
googleSignIn = () => {
this.setState({
formType: undefined,
});
this.onSocialLoginLink(SIGN_IN.WITH_GOOGLE.provider);
};
facebookSignIn = () => {
this.setState({
formType: undefined,
});
this.onSocialLoginLink(SIGN_IN.WITH_FACEBOOK.provider);
};
twitteSignIn = () => {
this.setState({
formType: undefined,
});
this.onSocialLoginLink(SIGN_IN.WITH_TWITTER.provider);
};
emailSignIn = values => {
const { email, password } = values;
const { firebase } = this.props;
firebase.auth
.signInWithEmailAndPassword(email, password)
.then(() => {
this.setState({ ...INITIAL_EMAIL_STATE });
// this.props.history.push(ROUTES.HOME);
})
.catch(error => {
this.setState({ error });
});
};
render() {
const { classes } = this.props;
const { error } = this.state;
const { saveRolesErr, isSavingRolesStarted } = this.props;
if (error && error.message) {
console.log(error.message);
}
return (
<MotionDiv>
<Page className={classes.root} title="Sign In">
<Box display="flex" flexDirection="column" height="100%" justifyContent="center">
<Container maxWidth="sm" className={classes.container}>
<Formik
initialValues={{
email: 'demo#devias.io',
password: 'Password123',
}}
validationSchema={Yup.object().shape({
email: Yup.string().email('Must be a valid email').max(255).required('Email is required'),
password: Yup.string().max(255).required('Password is required'),
})}
onSubmit={(values, { setSubmitting }) => {
setSubmitting(false);
const { formType } = this.state;
if (formType) {
if (formType === SIGN_IN.WITH_FACEBOOK.provider) {
this.facebookSignIn();
} else if (formType === SIGN_IN.WITH_GOOGLE.provider) {
this.googleSignIn();
} else if (formType === SIGN_IN.WITH_TWITTER.provider) {
this.twitteSignIn();
} else if (formType) {// BUT HERE I CAN'T SET THE formType FOR PASSWORD
this.emailSignIn(values);
}
}
}}
>
{({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values }) => (
<form onSubmit={handleSubmit}>
<Box mb={3}>
<Typography color="textPrimary" variant="h2">
Sign in
</Typography>
<Typography color="textSecondary" gutterBottom variant="body2">
Sign in on the internal platform
</Typography>
</Box>
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<Button
className={classes.facebook}
fullWidth
startIcon={<FacebookIcon />}
type="button"
onClick={this.submitFormType(SIGN_IN.WITH_FACEBOOK.provider, handleSubmit)}
size="large"
variant="contained"
>
SIGN in with Facebook
</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button
className={classes.google}
fullWidth
startIcon={<GoogleIcon />}
onClick={this.submitFormType(SIGN_IN.WITH_GOOGLE.provider, handleSubmit)}
size="large"
variant="contained"
>
Sign in with Google
</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button
className={classes.twitter}
fullWidth
startIcon={<TwitterIcon />}
onClick={this.submitFormType(SIGN_IN.WITH_TWITTER.provider, handleSubmit)}
size="large"
variant="contained"
>
Sign in with Twitter
</Button>
</Grid>
</Grid>
<Box mt={3} mb={1}>
<Typography align="center" color="textSecondary" variant="body1">
or login with email address
</Typography>
</Box>
<TextField
error={Boolean(touched.email && errors.email)}
fullWidth
helperText={touched.email && errors.email}
label="Email Address"
margin="normal"
name="email"
onBlur={handleBlur}
onChange={handleChange}
type="email"
value={values.email}
variant="outlined"
/>
<TextField
error={Boolean(touched.password && errors.password)}
fullWidth
helperText={touched.password && errors.password}
label="Password"
margin="normal"
name="password"
onBlur={handleBlur}
onChange={handleChange}
type="password"
value={values.password}
variant="outlined"
/>
<Box my={2}>
<Button
className={classes.button}
disabled={isSubmitting}
fullWidth
size="large"
type="submit"
variant="contained"
color="primary"
>
Sign in now
</Button>
</Box>
<Typography color="textSecondary" variant="body1">
Don&apos;t have an account?{' '}
<Link color="textSecondary" component={RouterLink} to="../register" variant="h6">
Sign up!
</Link>
</Typography>
</form>
)}
</Formik>
<div>{isSavingRolesStarted ? <Dots /> : null}</div>
<h1 style={{ margin: '8px', color: 'red', textAlign: 'center', backgroundColor: 'white' }}>
{error && error.message}
{saveRolesErr && saveRolesErr.message}
</h1>
</Container>
</Box>
</Page>
</MotionDiv>
);
}
}
const mapDispatchToProps = dispatch => ({
setUserRoleToUser: () => dispatch(changeToUserRole()),
});
const mapStateToProps = state => {
return {
isSavingRolesStarted: state.user.isSavingRolesStarted,
saveRolesErr: state.user.saveRolesErrMsg,
};
};
let LoginManagement = withStyles(useStyles)(LoginManagementBase);
const enhance = compose(withFirebase, connect(mapStateToProps, mapDispatchToProps), withEmailVerification);
LoginManagement = enhance(LoginManagement);
const LoginView = () => (
<AuthUserContext.Consumer>
{authUser => (
<div>
<LoginManagement authUser={authUser} />
</div>
)}
</AuthUserContext.Consumer>
);
const condition = authUser => authUser && authUser.roles.includes(ROLES.ANON);
export default withAuthorization(condition)(LoginView);
I could not solve this now so I did React vanilla for now.
import React from 'react';
import { connect } from 'react-redux';
import { compose } from 'recompose';
import { Link as RouterLink } from 'react-router-dom';
import { Box, Button, Container, Grid, Link, TextField, Typography } from '#material-ui/core';
import { withStyles } from '#material-ui/core/styles';
import Dots from 'react-activity/lib/Dots';
import { withFirebase } from '../../../../firebase';
import FacebookIcon from '../../../../assets/Facebook';
import GoogleIcon from '../../../../assets/Google';
import TwitterIcon from '../../../../assets/twitter-logo';
import Page from '../../utils/Page';
import MotionDiv from '../../utils/MotionDiv';
import { withEmailVerification, withAuthorization, AuthUserContext } from '../../../../session';
import { changeToUserRole } from '../../../../redux/userData/user.actions';
import * as ROLES from '../../../../constants/roles';
import 'react-activity/lib/Dots/Dots.css';
import * as SIGN_IN from '../../../../constants/signinmethods';
const useStyles = theme => ({
root: {
backgroundColor: theme.palette.primary.light,
minHeight: '100vh',
paddingBottom: theme.spacing(3),
paddingTop: theme.spacing(3),
},
container: {
backgroundColor: theme.palette.primary.main,
paddingTop: theme.spacing(3),
},
textField: {
boxShadow: theme.shadows[3],
backgroundColor: theme.palette.primary.light,
},
facebook: {
backgroundColor: '#3b5999',
color: 'white',
'&:hover': {
backgroundColor: '#4d70ba',
},
},
google: {
backgroundColor: '#ffffff',
textColor: 'black',
},
twitter: {
backgroundColor: '#ffffff',
textColor: 'black',
},
signInButton: {
backgroundColor: theme.palette.primary.main,
boxShadow: theme.shadows[5],
},
});
const INITIAL_EMAIL_STATE = {
email: '',
password: '',
error: null,
message: '',
isSubmitting: false,
};
class LoginManagementBase extends React.Component {
constructor() {
super();
this.state = {
...INITIAL_EMAIL_STATE,
};
this.facebookSignIn = this.facebookSignIn.bind(this);
this.googleSignIn = this.googleSignIn.bind(this);
this.twitteSignIn = this.twitteSignIn.bind(this);
}
onSocialLoginLink = provider => {
const { firebase, setUserRoleToUser } = this.props;
firebase.auth.currentUser
.linkWithPopup(firebase[provider])
.then(res => {
if (res.credential) {
if (res.user.email) {
firebase.doLogEvent(`linkWithPopup to Firestore for: ${res.user.email}`);
} else {
firebase.doLogEvent(`linkWithPopup to Firestore for: ${res.credential.providerId}`);
}
setUserRoleToUser();
}
})
.then(this.fetchSignInMethods)
.catch(error => {
if (error.code === 'auth/credential-already-in-use') {
const anonUser = firebase.auth.currentUser;
firebase.auth
.signInWithCredential(error.credential)
.then(res => {
if (res.user.email) {
firebase.doLogEvent(`signInWithCredential to Firestore for: ${res.user.email}`);
} else {
firebase.doLogEvent(`signInWithCredential to Firestore for: ${res.credential.providerId}`);
}
setUserRoleToUser();
// remove the anonUser implications?
// TODO: As anonymouse the User can't change the content(viewer only) so removing should not be a problem
anonUser
.delete()
.then(() => {
firebase.doLogEvent(`Deleted anonUser when signing in`);
})
.catch(err => {
firebase.doLogEvent(`Error deleted anonUser when signing in: ${err}`);
firebase.doLogEvent(`Manually remove anon account: ${anonUser.uid}`);
});
})
.catch(error => {
this.setState({ error, isSubmitting: false });
});
} else if (error.code === 'auth/email-already-in-use') {
error.message = `The email address ${error.email} is already in use by another account.`;
this.setState({ error, isSubmitting: false });
} else {
this.setState({ error, isSubmitting: false });
}
});
};
googleSignIn = () => {
this.setState({
isSubmitting: true,
});
this.onSocialLoginLink(SIGN_IN.WITH_GOOGLE.provider);
};
facebookSignIn = () => {
this.setState({
isSubmitting: true,
});
this.onSocialLoginLink(SIGN_IN.WITH_FACEBOOK.provider);
};
twitteSignIn = () => {
this.setState({
isSubmitting: true,
});
this.onSocialLoginLink(SIGN_IN.WITH_TWITTER.provider);
};
emailSignIn = () => {
const { email, password } = this.state;
const { firebase } = this.props;
firebase.auth
.signInWithEmailAndPassword(email, password)
.then(() => {
this.setState({ ...INITIAL_EMAIL_STATE });
// this.props.history.push(ROUTES.HOME);
})
.catch(error => {
this.setState({ error, isSubmitting: false });
});
};
mailHandle = e => {
const mailValue = e.target.value;
this.setState({
email: mailValue,
});
};
passwordHandle = e => {
const passwordValue = e.target.value;
this.setState({
password: passwordValue,
});
};
chekvalid = () => {
const { email, password } = this.state;
const isEmailValid = this.ValidEmail(email);
const isPasswordValid = this.ValidPass(password);
if (password.length === 0 && email.length === 0) {
this.setState({
error: 'Enter E-Mail and Password to continue!',
});
}
if (!isPasswordValid) {
this.setState({
message: 'Password should have more then 5 character',
});
}
if (email.length === 0) {
this.setState({
message: 'Enter an E-Mail to continue!',
});
}
if (password.length === 0) {
this.setState({
message: 'Enter an Password to continue!',
});
}
if (!isEmailValid) {
this.setState({
message: 'E-Mail is not valid!',
});
}
if (isEmailValid && isPasswordValid) {
this.setState({
message: '',
isSubmitting: true,
});
this.emailSignIn();
}
};
ValidEmail = email => {
const re = /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
};
ValidPass = email => {
if (email.length > 5) {
return true;
}
return false;
};
render() {
const { classes } = this.props;
const { message, error, email, password, isSubmitting } = this.state;
const { saveRolesErr, isSavingRolesStarted } = this.props;
if (error && error.message) {
console.log(error.message);
}
return (
<MotionDiv>
<Page className={classes.root} title="Sign In">
<Box display="flex" flexDirection="column" height="100%" justifyContent="center">
<Container maxWidth="sm" className={classes.container}>
<Box mb={3}>
<Typography color="textPrimary" variant="h2">
Sign in
</Typography>
<Typography color="textSecondary" gutterBottom variant="body2">
Sign in on the internal platform
</Typography>
</Box>
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<Button
className={classes.facebook}
disabled={isSubmitting}
fullWidth
startIcon={<FacebookIcon />}
type="submit"
onClick={this.facebookSignIn}
size="large"
variant="contained"
>
SIGN in with Facebook
</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button
className={classes.google}
disabled={isSubmitting}
fullWidth
type="submit"
startIcon={<GoogleIcon />}
onClick={this.googleSignIn}
size="large"
variant="contained"
>
Sign in with Google
</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button
className={classes.twitter}
disabled={isSubmitting}
fullWidth
type="submit"
startIcon={<TwitterIcon />}
onClick={this.twitteSignIn}
size="large"
variant="contained"
>
Sign in with Twitter
</Button>
</Grid>
</Grid>
<Box mt={3} mb={1}>
<Typography align="center" color="textSecondary" variant="body1">
or login with email address
</Typography>
</Box>
<form noValidate autoComplete="off">
<TextField
className={classes.textField}
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
value={email}
onChange={this.mailHandle}
color="secondary"
/>
</form>
<form noValidate autoComplete="off">
<TextField
className={classes.textField}
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
value={password}
onChange={this.passwordHandle}
color="secondary"
/>
</form>
<Box my={2}>
<Button
disabled={isSubmitting}
className={classes.signInButton}
fullWidth
size="large"
type="submit"
variant="contained"
color="primary"
onClick={this.chekvalid}
>
Sign in now
</Button>
</Box>
<Typography color="textSecondary" variant="body1">
Don&apos;t have an account?{' '}
<Link color="textSecondary" component={RouterLink} to="../register" variant="h6">
Sign up!
</Link>
</Typography>
<div>{isSavingRolesStarted ? <Dots /> : null}</div>
<h1 style={{ margin: '8px', color: 'red', textAlign: 'center', backgroundColor: 'white' }}>
{message}
{error && error.message}
{saveRolesErr && saveRolesErr.message}
</h1>
</Container>
</Box>
</Page>
</MotionDiv>
);
}
}
const mapDispatchToProps = dispatch => ({
setUserRoleToUser: () => dispatch(changeToUserRole()),
});
const mapStateToProps = state => {
return {
isSavingRolesStarted: state.user.isSavingRolesStarted,
saveRolesErr: state.user.saveRolesErrMsg,
};
};
let LoginManagement = withStyles(useStyles)(LoginManagementBase);
const enhance = compose(withFirebase, connect(mapStateToProps, mapDispatchToProps), withEmailVerification);
LoginManagement = enhance(LoginManagement);
const LoginView = () => (
<AuthUserContext.Consumer>
{authUser => (
<div>
<LoginManagement authUser={authUser} />
</div>
)}
</AuthUserContext.Consumer>
);
const condition = authUser => authUser && authUser.roles.includes(ROLES.ANON);
export default withAuthorization(condition)(LoginView);

Redux initial value null while clicking on edit button

I am trying to edit form. After first rendering I am not getting existing data from database. Because of state is null. If I click on second button first data is appearing. ( Because of state is null. If I click on second button first data is appearing. )
Reducer:
const intialState ={ channel:null}
Please click here for the image
//form Component
import React, { Fragment } from "react";
import Button from "#material-ui/core/Button";
import TextField from "#material-ui/core/TextField";
import Dialog from "#material-ui/core/Dialog";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import DialogTitle from "#material-ui/core/DialogTitle";
const FormChannel = ({ channelData, ouSubmit, open }) => {
let [formData, setFromData] = React.useState({
channel: channelData ? channelData.channel : "",
channelName: channelData ? channelData.channelName : "",
introduction: channelData ? channelData.introduction : "",
language: channelData ? channelData.language : "",
keywords: channelData ? channelData.keywords : ""
});
const { channel, channelName, introduction, language, keywords } = formData;
const handleClose = () => {
ouSubmit(formData);
setFromData({
channel: "",
channelName: "",
introduction: "",
language: "",
keywords: ""
});
};
const handleChange = channel => e => {
setFromData({ ...formData, [channel]: e.target.value });
};
const view = (
<Fragment>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Create Channel</DialogTitle>
<DialogContent>
<TextField
autoFocus
value={channel}
onChange={handleChange("channel")}
id="channel"
label="Name"
type="emachannelil"
margin="normal"
variant="outlined"
fullWidth
/>
<TextField
value={channelName}
onChange={handleChange("channelName")}
id="channelName"
label="Channel Name"
type="text"
margin="normal"
variant="outlined"
fullWidth
/>
<TextField
label="Language"
id="language"
value={language}
onChange={handleChange("language")}
type="text"
margin="normal"
variant="outlined"
fullWidth
/>
<TextField
value={introduction}
onChange={handleChange("introduction")}
id="introduction"
label="Introduction"
type="text"
margin="normal"
variant="outlined"
fullWidth
/>
<TextField
value={keywords}
onChange={handleChange("keywords")}
id="kewords"
label="Keywords"
type="text"
margin="normal"
variant="outlined"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary" variant="contained">
Create
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
return <Fragment>{view}</Fragment>;
};
export default FormChannel
Edit
import React, { Fragment } from "react";
import { connect } from "react-redux";
import { Button } from "#material-ui/core";
import FormChannel from "./Form";
const EditChannel = ({ channels: { channel }, open, setOpen }) => {
console.log(channel);
return (
<Fragment>
<FormChannel
open={open}
channelData={channel}
ouSubmit={formData => {
setOpen(false);
console.log(formData);
}}
/>
</Fragment>
);
};
const mapStateToProps = (state, props) => {
console.log(state);
return {
channels: state.channels
};
};
export default connect(mapStateToProps)(EditChannel);
card
import React, { Fragment } from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Typography, Button } from "#material-ui/core";
import Avatar from "#material-ui/core/Avatar";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { channelFollow, channelFetchById } from "../../../redux/action/channel";
import EditChannel from "../../Channels/List/Edit";
const useStyles = makeStyles(theme => ({
card: {
flexGrow: 1
},
img: {
height: "60%",
overflow: "hidden",
width: "100%"
},
link: {
textDecoration: "none",
color: "black"
},
link1: {
textDecoration: "none",
color: "white"
},
root: {
display: "flex",
"& > *": {
margin: theme.spacing(0.5)
},
justifyContent: "center"
},
button: {
display: "flex",
justifyContent: "center"
},
text: {
fontWeight: "bold",
fontSize: 15
},
text1: {
color: "gray",
fontSize: 15
},
bigAvatar: {
width: 140,
height: 140
},
buttons: {
marginRight: 5
}
}));
const ChannelCard = props => {
const classes = useStyles();
const { channel, channelFetchById } = props;
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const view = (
<div className={classes.card}>
<Link to={`/channels/${channel._id}`} className={classes.link}>
<div className={classes.root}>
<Avatar
alt="Remy Sharp"
src={channel.avatar}
className={classes.bigAvatar}
/>
</div>
<div className={classes.title}>
<Typography
variant="subtitle1"
align="center"
className={classes.text}
>
{channel.channelName}
</Typography>
<Typography variant="body2" align="center">
{channel.introduction}
</Typography>
</div>
</Link>
<Typography variant="body2" align="center" className={classes.text1}>
{channel.follows ? channel.follows.length : 0} followers <br />
Language:{channel.language}
</Typography>
<div className={classes.center}>
<div className={classes.button}>
<div className={classes.buttons}>
<Button
variant="contained"
color="primary"
onClick={() => {
channelFetchById(channel._id);
handleClickOpen();
}}
>
Edit
</Button>
{open ? <EditChannel setOpen={setOpen} open={open} /> : ""}
</div>
<div>
<Button color="primary" variant="contained">
Delete
</Button>
</div>
</div>
<br />
</div>
</div>
);
return <Fragment>{view}</Fragment>;
};
export default connect(null, { channelFollow, channelFetchById })(ChannelCard);
Reducer
import {
CHANNEL_FETCH,
CHANNEL_FETCH_BY_ID,
CHANNEL_UNFOLLOWED,
CHANNEL_EDIT
} from "../action/typeof";
const initialState = {
channels: [],
channel: null,
loading: true
};
export default (state = initialState, action) => {
const { payload } = action;
switch (action.type) {
case CHANNEL_FETCH:
return { ...state, channels: payload, loading: false };
case CHANNEL_FETCH_BY_ID:
return {
...state,
channel: payload,
loading: false
};
case CHANNEL_UNFOLLOWED:
return {
...state,
channelUnfollowedUser: payload,
loading: false
};
case CHANNEL_EDIT:
const channelEdit = state.channes.map(single =>
single._id === payload.id ? { single, ...payload.data } : single
);
return {
...state,
channels: channelEdit,
loading: false
};
default:
return state;
}
};

Resources