Redux initial value null while clicking on edit button - reactjs

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;
}
};

Related

React and Antd, form data destroys on fulllscreen drawer out of focus

I have tried to remove the destroy on close, use same form hook, different form hook, removed from hook, but nothing worked.
I have consoled logged the form data on show modal and close modal. Show modal has the form data but inside the log of close modal the data is reset to undefined. As soon as the modal opens the Data resets to null.
In the following code you can see the creation of Full screen modal which is made using AntDrawer:
import React, { useEffect, useState } from "react";
import FullscreenDialog from "Components/FullScreenDialog";
import Form from "Components/Form";
import { Input, Typography, Button, Space, Menu, Dropdown } from "antd";
import { PlusCircleOutlined, CloseOutlined, DownOutlined } from "#ant-design/icons";
import HeaderInput from "Components/Inputs/HeaderInput";
import Table from "Components/Table";
import styled from "styled-components";
import { Services } from "Utilities/network";
import DetailsModal from "./DetailsModal";
const RootWrapper = styled.div`
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
`;
const CreateVoucher = ({ visible, initalValue = {}, onSuccess = (e) => e, onClose = (e) => e }) => {
const [form] = Form.useForm();
const [voucherData, setVoucherData] = useState([]);
const [newModal, setNewModal] = useState(false);
const [formData, setFormData] = useState({});
const handleSubmit = async () => {
const { _id, name, header } = form.getFieldsValue(true);
if (name && header && voucherData) {
let resp = await Services.createVoucher({
_id,
name,
header,
voucherData,
});
if (resp.key) onSuccess();
}
};
if (initalValue) {
const { _id, name, header, voucherData } = initalValue;
form.setFieldsValue({
_id,
name,
header,
voucherData,
});
}
useEffect(() => {
const { voucherData } = initalValue;
if (voucherData && voucherData.length) setVoucherData(voucherData);
else setVoucherData([]);
}, [initalValue]);
const handleDelete = (index) => {
const tableData = voucherData.filter((data, dataIndex) => {
if (index !== dataIndex) return data;
return null;
});
setVoucherData(tableData);
};
return (
<>
<FullscreenDialog visible={visible} onClose={onClose}>
<FullscreenDialog.Paper>
<RootWrapper>
<Typography.Text strong style={{ fontSize: "30px" }}>
Generate Voucher
</Typography.Text>
<Form form={form}>
<Space direction="vertical" style={{ width: "100%" }}>
<Form.Item label="Voucher ID" name="name" rules={[{ required: true, message: "Please input ID" }]}>
<Input />
</Form.Item>
<HeaderInput
fieldProps={{
name: "header",
label: "Inoivce Header",
rules: [{ required: true, message: "Please select header" }],
}}
itemProps={{ placeholder: "Select Header for Voucher" }}
/>
<Space style={{ float: "right", marginTop: "20px" }}>
<Button
type="primary"
shape="round"
icon={<PlusCircleOutlined />}
onClick={() => {
setFormData(form.getFieldsValue(true));
setNewModal(true);
}}
>
Add Details
</Button>
</Space>
<Table
dataSource={voucherData}
count={voucherData?.length || 0}
columns={[
{
title: "Label",
key: "label",
dataIndex: "label",
render: (_, record) => record.label,
},
{
title: "Details",
key: "detail",
dataIndex: "detail",
render: (_, record) => record.detail,
},
{
align: "right",
render: (_, record, index) => {
return (
<Dropdown
trigger={["click"]}
overlay={
<Menu>
<Menu.Item
style={{ color: "red " }}
onClick={() => {
handleDelete(index);
}}
>
<CloseOutlined /> Delete
</Menu.Item>
</Menu>
}
>
<Button type="primary">
Edit
<DownOutlined />
</Button>
</Dropdown>
);
},
},
]}
/>
<Space style={{ float: "right", marginTop: "30px" }}>
<Button type="outline" onClick={onClose}>
Cancel
</Button>
<Button type="primary" htmlType="submit" onClick={handleSubmit}>
Submit
</Button>
</Space>
</Space>
</Form>
</RootWrapper>
</FullscreenDialog.Paper>
</FullscreenDialog>
<DetailsModal
visible={newModal}
onClose={() => {
setNewModal(false);
console.log(formData);
form.setFieldsValue({ name: "2222" });
console.log(form.getFieldsValue(true));
}}
onSuccess={(data) => {
setVoucherData([...voucherData, data]);
setNewModal(false);
form.setFieldsValue(formData);
}}
/>
</>
);
};
export default CreateVoucher;
Below is the code for modal to use another form:
import React from "react";
import { Modal, Space, Input, Button } from "antd";
import Form from "Components/Form";
import styled from "styled-components";
const RootWrapper = styled.div`
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
`;
const DetailsModal = ({ visible, onClose = (e) => e, onSuccess = (e) => e }) => {
const [form] = Form.useForm();
return (
<Modal
visible={visible}
destroyOnClose={true}
onClose={() => {
form.resetFields();
onClose();
}}
onCancel={() => {
form.resetFields();
onClose(false);
}}
title="Add Details"
footer={false}
>
<Form form={form}>
<Space direction="vertical" style={{ width: "100%" }}>
<RootWrapper>
<Form.Item name="label" label="Label" rules={[{ required: true, message: "Please input label" }]}>
<Input />
</Form.Item>
<Form.Item name="detail" label="Details" rules={[{ required: true, message: "Please input details" }]}>
<Input />
</Form.Item>
</RootWrapper>
<Space style={{ float: "right", marginTop: "20px" }}>
<Button
type="outline"
onClick={() => {
form.resetFields();
onClose();
}}
>
Cancel
</Button>
<Button
type="primary"
htmlType="submit"
onClick={() => {
const { detail, label } = form.getFieldsValue(true);
onSuccess({ detail, label });
}}
>
Submit
</Button>
</Space>
</Space>
</Form>
</Modal>
);
};
export default DetailsModal;

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);

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);

Material UI TextField Not Showing Value

I have a form in react where I'm using a Material UI TextField. I want a value present in the TextField and I can see that it is being set when I use the Inspect option of Google Chrome. In addition to that, I see that the type="hidden". I have changed this to type="text" and to no avail, the value is still not presented. Curious if anyone here has any understanding as to why this would happen. Below is the primary code that is causing the problem:
<TextField
name="Property"
select
fullWidth
margin="normal"
className={clsx(selectInputStyle.margin, selectInputStyle.textField, selectInputStyle.root)}
value={viewProperties[index].name}
onChange={this.handleSelectChange}
>
{propertyKeys.map((key, index) => (
<MenuItem value={key} key={index}>
{key}
</MenuItem>
))}
</TextField>
Here is the full code file just for a complete context of what is going on.
import React, { Component } from 'react';
import { Container, Form, Button, Row, Col, Nav, NavItem, NavLink, Input, FormGroup } from 'reactstrap';
import { connect } from 'react-redux';
import { reduxForm, FieldArray, arrayRemoveAll } from 'redux-form/immutable';
import * as Immutable from 'immutable';
import _ from 'lodash';
import { bindActionCreators } from 'redux';
import BlockUi from 'react-block-ui';
import MaterialButton from '#material-ui/core/Button';
import DeleteIcon from '#material-ui/icons/Delete';
import { makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import MenuItem from '#material-ui/core/MenuItem';
import Select from '#material-ui/core/Select';
import InputMaterial from '#material-ui/core/Input';
import FormControl from '#material-ui/core/FormControl';
import TextField from '#material-ui/core/TextField';
import OutlinedInput from '#material-ui/core/OutlinedInput';
import clsx from 'clsx';
import { AvText, AvSelect } from '../forms/components';
import { showNotification, loadPayerProperties, updatePayerProperties } from '../actions';
class PayerPropertiesEditor extends Component {
constructor(props) {
super(props);
this.uploadRef = React.createRef();
this.state = {
errors: [],
refeshProperties: false,
blocking: false
};
this.showButton = false;
this.divPadding = { padding: '20px' };
this.doSubmit = this.doSubmit.bind(this);
this.handleInvalidSubmit = this.handleInvalidSubmit.bind(this);
this.renderProperties = this.renderProperties.bind(this);
this.handleSelectChange = this.handleSelectChange.bind(this);
this.useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1)
},
leftIcon: {
marginRight: theme.spacing(1)
},
rightIcon: {
marginLeft: theme.spacing(1)
},
iconSmall: {
fontSize: 20
},
root: {
display: 'flex',
flexWrap: 'wrap'
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
},
container: {
display: 'flex',
flexWrap: 'wrap'
},
input: {
margin: theme.spacing(1)
}
}));
}
componentDidMount() {
this.setState({ view: 'payer' });
}
componentDidUpdate(prevProps) {
const { loadPayerProperties } = this.props;
if (this.state.refeshProperties) {
this.props.arrayRemoveAll('payerPropertiesEditorForm', 'properties');
loadPayerProperties();
this.setState({ refeshProperties: false });
this.setState({ blocking: false });
this.showButton = false;
}
if (!prevProps.properties && this.props.properties) {
this.props.change('properties', Immutable.fromJS(this.props.properties));
}
}
doSubmit(values) {
const { updatePayerProperties } = this.props;
return new Promise(resolve => {
this.setState({
blocking: true
});
updatePayerProperties(values.toJS(), () => {
this.setState({ refeshProperties: true });
});
resolve();
});
}
handleInvalidSubmit() {
this.props.showNotification({
level: 'error',
message: 'Errors were found.'
});
}
handleSelectChange(event) {
console.log(event);
}
renderProperties({ fields }) {
const inputUseStyles = makeStyles(theme => ({
root: {
display: 'flex',
flexWrap: 'wrap'
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
},
container: {
display: 'flex',
flexWrap: 'wrap'
},
margin: {
margin: theme.spacing(1)
},
textField: {
flexBasis: 200
},
input: {
margin: theme.spacing(1)
}
}));
const selectInputStyle = inputUseStyles().input;
const useStyles = this.useStyles();
const { formProperties, unsetPropertiesKeys } = this.props;
const viewProperties = formProperties[`${this.state.view}`];
const propertyKeys = unsetPropertiesKeys[`${this.state.view}`];
return (
<div maxWidth="sm" className={selectInputStyle.root}>
{fields.map((property, index) => (
<Row
key={index}
style={{
display: viewProperties[index].action === 'remove' ? 'none' : ''
}}
>
<Col xs={5}>
<TextField
select
fullWidth
margin="normal"
className={clsx(selectInputStyle.margin, selectInputStyle.textField, selectInputStyle.root)}
value={viewProperties[index].name}
onChange={this.handleSelectChange}
>
{propertyKeys.map((key, index) => (
<MenuItem value={key} key={index}>
{key}
</MenuItem>
))}
</TextField>
</Col>
<Col xs={5}>
<AvText
name={`${property}.value`}
onChange={() => {
if (viewProperties[index].action !== 'add') {
this.props.change(`${property}.action`, 'update');
this.showButton = true;
}
}}
/>
</Col>
<Col xs={1}>
<MaterialButton
variant="contained"
className="{classes.button}"
onClick={() => {
fields.remove(index);
if (viewProperties[index].action !== 'add') {
fields.insert(
index,
Immutable.fromJS(Object.assign({}, viewProperties[index], { action: 'remove' }))
);
this.showButton = true;
}
}}
>
Delete
<DeleteIcon className={useStyles.rightIcon} />
</MaterialButton>
</Col>
</Row>
))}
<Row>
<Col xs={12}>
<Button
color="primary"
onClick={() => {
fields.push(
Immutable.fromJS({
owner: this.props.ownerKeys[this.state.view],
action: 'add'
})
);
this.showButton = true;
}}
>
Add Property
</Button>
</Col>
</Row>
<br />
{this.showButton === true && (
<Row>
<Col xs={12}>
<Button color="primary" type="submit">
Submit
</Button>
</Col>
</Row>
)}
</div>
);
}
render() {
const { handleSubmit, properties, payerName, chsId, parentChsId } = this.props;
const formStyles = makeStyles(theme => ({
root: {
display: 'flex',
flexWrap: 'wrap'
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
const navItems = ['payer', 'clearinghouse', 'parent'].map(key => (
<Tab
textColor="primary"
key={key}
label={
key === 'payer'
? 'PAYER: ' + payerName
: key === 'clearinghouse'
? 'CLEARING HOUSE: ' + chsId
: 'PARENT: ' + parentChsId
}
onClick={() => this.setState({ view: key })}
/>
));
const overrides = this.state.view === 'payer' ? ['clearinghouse'] : this.state.view === 'parent' ? ['parent'] : [];
const readonly = properties
? overrides
.filter(key => key !== this.state.view)
.map(key => properties[key])
.reduce((acc, val) => acc.concat(val), [])
.map((property, idx) => {
return (
<Row key={idx}>
<Col xs={5}>
<FormGroup>
<Input value={property.name} disabled />
</FormGroup>
</Col>
<Col xs={5}>
<FormGroup>
<Input value={property.value} disabled />
</FormGroup>
</Col>
</Row>
);
})
: [];
return (
<BlockUi tag="div" blocking={this.state.blocking} className="my-2">
<Container maxWidth="sm">
<AppBar position="static" color="default">
<Tabs variant="fullWidth" textColor="primary">
{navItems}
</Tabs>
</AppBar>
<FormControl
fullWidth
className="mt-4"
onSubmit={handleSubmit(this.doSubmit)}
ref={form => (this.formRef = form)}
>
{readonly}
<FieldArray
name={`properties.${this.state.view}`}
component={this.renderProperties}
rerenderOnEveryChange
/>
</FormControl>
</Container>
</BlockUi>
);
}
}
const mapStateToProps = state => {
const {
payerPropertiesStore: {
payer: { payerId, payerName, chsId, parentChsId },
properties,
propertyKeys
},
form: {
payerPropertiesEditorForm: {
values: { properties: formProperties }
}
}
} = state.toJS();
const unsetPropertiesKeys = {};
for (const view of ['payer', 'clearinghouse', 'parent']) {
unsetPropertiesKeys[view] = propertyKeys.filter(key => !_.find(formProperties[view], { name: key }));
}
const ownerKeys = { payer: payerId, clearinghouse: chsId, parent: parentChsId };
return { formProperties, properties, ownerKeys, unsetPropertiesKeys, payerId, payerName, chsId, parentChsId };
};
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
showNotification,
loadPayerProperties,
updatePayerProperties,
arrayRemoveAll
},
dispatch
);
export default reduxForm({
form: 'payerPropertiesEditorForm',
enableReinitialize: true,
initialValues: Immutable.fromJS({ properties: {} })
})(
connect(
mapStateToProps,
mapDispatchToProps
)(PayerPropertiesEditor)
);
In the TextField you are setting value from viewProperties like,
value={viewProperties[index].name}
You are getting data in viewProperties from formProperties based on this.state.view
const viewProperties = formProperties[`${this.state.view}`];
As you are setting view state in componentDidMount, on intial render view is not set and don't have value so you are not getting any value from formProperties.
You need to have a default state,
this.state = {
errors: [],
refeshProperties: false,
blocking: false,
view: 'payer' // default set
};

React is not pushing items in array

I'm trying to contain my items within one array, however it keeps creating a new array for each item that is pushed in the array. Which makes it impossible to loop through items within an array
And of course I referred this and I have this part right
Lists and Keys
Working Demo
https://codesandbox.io/embed/0xv3104ll0
App.js
import React, {Component} from 'react';
import Navbar from './components/Navbar';
import {withStyles} from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import logo from './logo.svg';
import {Typography, Button} from '#material-ui/core';
import Footer from './components/Footer';
import Emoji from './components/Emoji';
import TextField from '#material-ui/core/TextField';
import EmojiPicker from 'emoji-picker-react';
import JSEMOJI from 'emoji-js';
import Icon from '#material-ui/core/Icon';
let jsemoji = new JSEMOJI();
// set the style to emojione (default - apple)
jsemoji.img_set = 'emojione';
// set the storage location for all emojis
jsemoji.img_sets.emojione.path = 'https://cdn.jsdelivr.net/emojione/assets/3.0/png/32/';
// some more settings...
jsemoji.supports_css = false;
jsemoji.allow_native = true;
jsemoji.replace_mode = 'unified'
const styles = theme => ({
shadows: ["none"],
spacing: 8,
root: {
flexGrow: 1,
minHeight: '800px',
width: '100%',
position: 'relative'
},
paper: {
padding: theme.spacing.unit * 2,
textAlign: 'left',
width: '500px',
color: theme.palette.text.secondary
},
textField: {
width: '400px'
},
myitem: {
margin: '40px'
},
emoji: {
margin: '40px'
},
emojiButton: {
margin: '20px 0px'
},
myitemList:{
margin:'20px 0px'
},
notFound: {
margin:'20px 0px'
},
cancel: {
margin: '20px 0px'
}
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
emoji: '',
text: '',
items: [],
emojiToggle: false
}
}
onChange = (e) => {
e.preventDefault()
this.setState({text: e.target.value});
}
handleClick = (n, e) => {
let emoji = jsemoji.replace_colons(`:${e.name}:`);
this.setState({
text: this.state.text + emoji,
});
// console.log(this.state.items)
}
handleButton = (e) => {
e.preventDefault();
if(!this.state.emojiToggle){
this.setState({emojiToggle: true})
}
else{
this.setState({emojiToggle: false})
}
}
onSubmit = () => {
let myArray = []
myArray.push(this.state.text)
this.setState({
text: this.state.text,
items: [...myArray]
}, () => console.log(this.state.items));
}
render() {
const {classes} = this.props;
return (
<div className={classes.root}>
<Navbar/>
<Grid container spacing={12}>
<Grid item sm={6} className={classes.myitem}>
<Paper className={classes.paper}>
<Typography variant="h2" component="h2">
Insert An Emoji
</Typography>
{/* Begin Form */}
<form>
<TextField
id="standard-name"
label="Enter Something"
className={classes.textField}
value={this.state.text}
onChange={this.onChange}
margin="normal"
/>
{this.state.emojiToggle ? (
<div>
<EmojiPicker onEmojiClick={this.handleClick}/>
<Button
className={classes.cancel}
onClick={this.handleButton}
color="danger"
variant="outlined">
Close
</Button>
</div>
)
: (
<div>
<Button onClick={this.handleButton} color="primary" variant="outlined">
Show Emojis
</Button>
<Button onClick={this.onSubmit} style={{ marginLeft: '10px'}} color="primary" variant="outlined">
Submit
</Button>
</div>
)}
{/* End Form */}
</form>
</Paper>
</Grid>
<Grid item sm={4} className={classes.myitem}>
<Typography variant="h2" component="h2">
Output
</Typography>
{this.state.items.length > 0 ? (
this.state.items.map( (item, i) => (
<div key={i}>
<Grid item sm={8} className={classes.myitemList}>
<Paper >
<Typography>
{item}
</Typography>
</Paper>
</Grid>
</div>
))
) : (
<div>
<Grid item sm={6} className={classes.notFound}>
<Typography>
No Items
</Typography>
</Grid>
</div>
)}
</Grid>
</Grid>
<Footer/>
</div>
);
}
}
export default withStyles(styles)(App);
Replace your onSubmit Function from this code.
onSubmit = e => {
e.preventDefault();
this.setState(
{
text: this.state.text,
items: [...this.state.items, this.state.text]
},
() => console.log(this.state.items)
);
};
change onSubmit method with this.
onSubmit = e => {
e.preventDefault();
this.setState(
{
text: this.state.text,
items: [this.state.text]
},
() => console.log(this.state.items)
);
};
look into your working demo link onSubmit function it's correct one.
Since you want to list all the emoji items, change your OnSubmit to following:
onSubmit = () => {
const {
state: { text, items = [] }
} = this;
const itemList = [...items];
itemList.push(text);
this.setState(
{
text: text,
items: itemList
},
() => console.log(this.state.items)
);
};
this will update items array and not create new one.

Resources