I'm using Material UI's "Dialog" component to open a separate mini-form upon a button click event. However, the Dialog box is actually opening up on page render. And I'm also unable to close it by clicking the box's "Cancel" button. Not to mention, whenever I attempt to type in anything into either of the Dialog box's textfields: my client crashes with an error of "TypeError: this.setState is not a function" for the line of: onChange={(e) => this.setState({ playlistName: e.target.value })}
import React, { Component } from 'react';
import { ThemeProvider } from '#material-ui/core';
import Grid from '#material-ui/core/Grid';
import Button from '#material-ui/core/Button';
import FormLabel from '#material-ui/core/FormLabel';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import TextField from '#material-ui/core/TextField';
import { makeStyles, withStyles } from '#material-ui/core/styles';
import { DataGrid } from '#material-ui/data-grid';
import Footer from './Footer';
import theme from '../../styles/MuiTheme';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemAvatar from '#material-ui/core/ListItemAvatar';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import Avatar from '#material-ui/core/Avatar';
import IconButton from '#material-ui/core/IconButton';
import FormGroup from '#material-ui/core/FormGroup';
import Checkbox from '#material-ui/core/Checkbox'
import Typography from '#material-ui/core/Typography';
import DeleteIcon from '#material-ui/icons/Delete';
import EditIcon from '#material-ui/icons/Edit';
import ListItemSecondaryAction from '#material-ui/core/ListItemSecondaryAction';
import QueueMusicIcon from '#material-ui/icons/QueueMusic';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
const styles = theme => ({
root: {
flexGrow: 1,
'& > *': {
margin: theme.spacing(1),
width: '25ch',
maxWidth: 752
},
control: {
padding: theme.spacing(2)
},
button: {
primary: "#1DB954"
},
listAllPlaylists: {
backgroundColor: theme.palette.background.paper,
overflow: "auto",
maxHeight: "10"
},
title: {
margin: theme.spacing(4, 0, 2)
}
}
});
function generate(element) {
return [0].map((value) =>
React.cloneElement(element, {
key: value
}),
);
}
class EditPlaylistDetails extends Component {
constructor(props) {
super(props);
this.state = {
artist: '',
album: '',
song: '',
playlistName: '',
description: '',
playlistData: [],
allPlaylists: [],
open: false,
secondary: false
}
// this.displayExistingPlaylists = this.displayExistingPlaylists.bind(this);
// this.displayLivePlaylistData = this.displayLivePlaylistData.bind(this);
this.editModal = this.editModal.bind(this)
this.handlePlaylistDelete = this.handlePlaylistDelete.bind(this)
this.handlePlaylistEdit = this.handlePlaylistEdit.bind(this)
this.handleClickOpen = this.handleClickOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
};
handleSecondary = () => {
this.setState({ secondary: true });
};
// handleDense = () => {
// this.setState({ dense: true });
// };
componentDidMount() {
fetch('http://localhost:5040/playlist/', {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json',
'Authorization': this.props.sessionToken
})
}).then((response) => response.json())
.then((res) => {
console.log(res);
return res;
}).then((res) => {
this.setState({
allPlaylists: res,
playlistData: res
})
})
.catch((err) => { console.log(err) })
};
handlePlaylistDelete(playlistDeleteId, userId) {
console.log(playlistDeleteId)
fetch(`http://localhost:5040/playlist/delete/${playlistDeleteId}`, {
method: 'DELETE',
body: JSON.stringify({
playlist: {
playlistId: this.props.playlistIdProp,
playlistName: this.state.playlistName,
description: this.state.description
}
}),
headers: new Headers({
'Content-Type': 'application/json',
'Authorization': this.props.sessionToken
})
})
.then(res => res.json())
.catch(err => console.log(err))
};
handlePlaylistEdit(playlistUpdateId) {
playlistUpdateId.preventDefault();
fetch(`http://localhost:5040/playlist/update/${playlistUpdateId}`, {
method: 'PUT',
body: JSON.stringify({
playlist: {
playlistId: this.props.playlistIdProp,
playlistName: this.state.playlistName,
description: this.state.description
}
}),
headers: new Headers({
'Content-Type': 'application/json',
'Authorization': this.props.sessionToken
})
}).then(
(response) => response.json()
).then((allPlaylistsResponse) => {
console.log(this.state.open)
});
};
editModal(playlistId) {
return (
<div>
<form onSubmit={() => this.handlePlaylistEdit(playlistId)} noValidate autoComplete="off" style={{ marginTop: '2rem' }}>
<TextField size="small" id="outlined-basic standard-size-small" label="Artist / Band" variant="filled" style={{ backgroundColor: 'white', color: 'white', borderRadius: '10px' }} onChange={(e) => this.setState({ artist: e.target.value })} value={this.state.artist} />
<TextField size="small" id="outlined-basic standard-size-small" label="Album" variant="filled" style={{ backgroundColor: 'white', color: 'white', borderRadius: '10px' }} onChange={(e) => this.setState({ album: e.target.value })} value={this.state.album} />
<TextField size="small" id="outlined-basic standard-size-small" label="Song" variant="filled" style={{ backgroundColor: 'white', color: 'white', borderRadius: '10px' }} onChange={(e) => this.setState({ song: e.target.value })} value={this.state.song} />
<br />
<Button variant="contained" color="primary" type="submit">Add Info to Playlist</Button>
</form>
</div>
)
};
handleClickOpen() {
this.setState = ({
open: true
})
};
handleClose() {
this.setState = ({
open: false
})
};
render() {
const { classes } = this.props;
return (
<ThemeProvider theme={theme}>
<div style={{ width: '100%', marginTop: '80px' }}>
<h1>View / Edit Your Created Playlists:</h1>
{/* <div >
{this.displayExistingPlaylists()}
</div> */}
<Grid container direction="column" alignContent="center" spacing={2} className={this.props.classes.root}>
<Grid item xs={12} md={6}>
<div style={{ textAlign: "-webkit-center", maxHeight: 400, overflow: 'auto' }}>
{this.state.allPlaylists.map((allPlaylistsCreated, index) => {
console.log(this.state.allPlaylists)
console.log(allPlaylistsCreated)
const { classes } = this.props;
return (
<Grid key={index} item xs={12} md={6} style={{ maxHeight: '100px', overflow: 'auto', textAlign: "center" }}>
<div className={classes.listAllPlaylists}>
<List>
{generate(
<ListItem>
<ListItemAvatar>
<Avatar>
<QueueMusicIcon key={index} style={{ color: "#191414" }} />
</Avatar>
</ListItemAvatar>
<ListItemText
key={index}
primary={allPlaylistsCreated.playlistName}
secondary={allPlaylistsCreated.description}
style={{ color: "white" }}
/>
<ListItemSecondaryAction>
<IconButton edge="start" aria-label="edit">
<EditIcon key={index} style={{ color: "#1DB954" }} onClick={this.handleClickOpen(allPlaylistsCreated.id)} />
</IconButton>
<IconButton edge="end" aria-label="delete">
<DeleteIcon key={index} style={{ color: "red" }} onClick={() => this.handlePlaylistDelete(allPlaylistsCreated.id, allPlaylistsCreated.userId)} />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
)}
</List>
</div>
</Grid>
)
})}
</div>
</Grid>
</Grid>
<form onSubmit={this.handlePlaylistEdit}>
<Dialog open={this.handleClickOpen} onClose={this.handleClose}>
<DialogTitle>Update Playlist Details:</DialogTitle>
<DialogContent>
<DialogContentText>
You may update your playlist information below:
</DialogContentText>
<TextField
margin='dense'
id='playlistName'
label='Playlist Name:'
fullWidth
onChange={(e) => this.setState({ playlistName: e.target.value })}
value={this.state.playlistName}
required
/>
<br />
<TextField
margin='dense'
id='description'
label='Description:'
fullWidth
onChange={(e) => this.setState({ description: e.target.value })}
value={this.state.description}
/>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color='primary'>
Cancel
</Button>
<Button onClick={this.handlePlaylistEdit} color='primary'>
Update Playlist
</Button>
</DialogActions>
</Dialog>
</form>
</div>
</ThemeProvider >
)
}
}
export default withStyles(styles, { withTheme: true })(EditPlaylistDetails);
syntax is wrong for setState
handleClickOpen() {
this.setState ({
open: true
})
}
handleClose() {
this.setState({
open: false
})
}
also correct below open value binding to {this.state.open}
<Dialog open={this.state.open} .... />
You are passing something for handleClickOpen() method, but not accepting any parameter in function definition.
<EditIcon key={index} style={{ color: "#1DB954" }} onClick={this.handleClickOpen(**allPlaylistsCreated.id**)} />
Related
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.
I am working on a next.js theme. I want to navigate to the "dashboards/analytics" page upon successfull authentication. we are using JWT based authentication. Authentication is done. but i could not navigate to the dashboards page.I really could not understand what is wrong in this code. Also i am getting a "Password is required error" even after passing a value. but it is not effecting the login function(i was able to login though, with the same password value). i tried commenting out the "yup resolver" code. but it didnt show any error when i clicked login button without a password(but login was not successfull). Kindly someone help me to fix these issues.
AuthContext.js file is as shown below
// ** React Imports
import { createContext, useEffect, useState } from 'react'
// ** Next Import
import {useRouter} from 'next/router'
// ** Axios
import axios from 'axios'
// ** Config
import authConfig from 'src/configs/auth'
// ** Defaults
const defaultProvider={
user: null,
loading: true,
setUser: () => null,
setLoading: () => Boolean,
isInitialized: false,
login: (token) => {},
logout: () => Promise.resolve(),
setIsInitialized: () => Boolean,
register: () => Promise.resolve(),
token:'',
};
const AuthContext = createContext(defaultProvider)
const AuthProvider = ({ children }) => {
// ** States
const [user, setUser] = useState(defaultProvider.user)
const [loading, setLoading] = useState(defaultProvider.loading)
const [isInitialized, setIsInitialized] = useState(defaultProvider.isInitialized)
const [token,setToken]=useState(defaultProvider.token)
// ** Hooks
const router = useRouter()
useEffect(() => {
const initAuth = async () => {
setIsInitialized(true)
const storedToken = window.localStorage.getItem(authConfig.storageTokenKeyName)
if (storedToken) {
setLoading(true)
await axios
.get(authConfig.meEndpoint, {
headers: {
Authorization: storedToken
}
})
.then(async response => {
setLoading(false)
setUser({ ...response.data.userData })
})
} else {
setLoading(false)
}
}
initAuth()
}, [])
const handleLogin=(token)=>{
setToken(token);
router.push('/dashboards/analytics');
}
const handleLogout = () => {
setUser(null)
setIsInitialized(false)
window.localStorage.removeItem('userData')
window.localStorage.removeItem(authConfig.storageTokenKeyName)
router.push('/login')
}
const handleRegister = (params, errorCallback) => {
axios
.post(authConfig.registerEndpoint, params)
.then(res => {
if (res.data.error) {
if (errorCallback) errorCallback(res.data.error)
} else {
handleLogin({ email: params.email, password: params.password })
}
})
.catch(err => (errorCallback ? errorCallback(err) : null))
}
const values = {
user,
loading,
setUser,
setLoading,
isInitialized,
setIsInitialized,
login: handleLogin,
logout: handleLogout,
register: handleRegister,
token
}
return <AuthContext.Provider value={values}>{children}</AuthContext.Provider>
}
export { AuthContext, AuthProvider }
Login/index.js file is as shown below,
// ** React Imports
import { useState, useContext } from 'react'
import {useRouter} from 'next/router'
// ** Next Imports
import Link from 'next/link'
import axios from 'axios'
// ** MUI Components
import Button from '#mui/material/Button'
import Divider from '#mui/material/Divider'
import Checkbox from '#mui/material/Checkbox'
import TextField from '#mui/material/TextField'
import InputLabel from '#mui/material/InputLabel'
import IconButton from '#mui/material/IconButton'
import Box from '#mui/material/Box'
import FormControl from '#mui/material/FormControl'
import useMediaQuery from '#mui/material/useMediaQuery'
import OutlinedInput from '#mui/material/OutlinedInput'
import { styled, useTheme } from '#mui/material/styles'
import FormHelperText from '#mui/material/FormHelperText'
import InputAdornment from '#mui/material/InputAdornment'
import Typography from '#mui/material/Typography'
import MuiFormControlLabel from '#mui/material/FormControlLabel'
// ** Icons Imports
import Google from 'mdi-material-ui/Google'
import Github from 'mdi-material-ui/Github'
import Twitter from 'mdi-material-ui/Twitter'
import Facebook from 'mdi-material-ui/Facebook'
import EyeOutline from 'mdi-material-ui/EyeOutline'
import EyeOffOutline from 'mdi-material-ui/EyeOffOutline'
// ** Third Party Imports
import * as yup from 'yup'
import { useForm, Controller } from 'react-hook-form'
import { yupResolver } from '#hookform/resolvers/yup'
// ** Hooks
import { useAuth } from 'src/hooks/useAuth'
import useBgColor from 'src/#core/hooks/useBgColor'
import { useSettings } from 'src/#core/hooks/useSettings'
// ** Configs
import themeConfig from 'src/configs/themeConfig'
// ** Layout Import
import BlankLayout from 'src/#core/layouts/BlankLayout'
// ** Demo Imports
import FooterIllustrationsV2 from 'src/views/pages/auth/FooterIllustrationsV2'
import { AuthContext } from 'src/context/AuthContext'
// ** Styled Components
const LoginIllustrationWrapper = styled(Box)(({ theme }) => ({
padding: theme.spacing(20),
paddingRight: '0 !important',
[theme.breakpoints.down('lg')]: {
padding: theme.spacing(10)
}
}))
const LoginIllustration = styled('img')(({ theme }) => ({
maxWidth: '48rem',
[theme.breakpoints.down('lg')]: {
maxWidth: '35rem'
}
}))
const RightWrapper = styled(Box)(({ theme }) => ({
width: '100%',
[theme.breakpoints.up('md')]: {
maxWidth: 450
}
}))
const BoxWrapper = styled(Box)(({ theme }) => ({
[theme.breakpoints.down('xl')]: {
width: '100%'
},
[theme.breakpoints.down('md')]: {
maxWidth: 400
}
}))
const TypographyStyled = styled(Typography)(({ theme }) => ({
fontWeight: 600,
marginBottom: theme.spacing(1.5),
[theme.breakpoints.down('md')]: { marginTop: theme.spacing(8) }
}))
const LinkStyled = styled('a')(({ theme }) => ({
fontSize: '0.875rem',
textDecoration: 'none',
color: theme.palette.primary.main
}))
const FormControlLabel = styled(MuiFormControlLabel)(({ theme }) => ({
'& .MuiFormControlLabel-label': {
fontSize: '0.875rem',
color: theme.palette.text.secondary
}
}))
const schema = yup.object().shape({
username: yup.string().required(),
password: yup.string().min(5 ).required()
})
const LoginPage = () => {
const [showPassword, setShowPassword] = useState(false)
const [username,setUsername]=useState('');
const [password,setPassword]=useState('');
//const [errMsg,setErrMsg]=useState('');
// ** Hooks
const auth = useContext(AuthContext);
const theme = useTheme()
const router=useRouter();
const bgClasses = useBgColor()
const {
settings: { skin }
} = useSettings()
const {
control,
setError,
//handleSubmit,
formState: { errors }
} = useForm({
//defaultValues,
mode: 'onBlur',
resolver: yupResolver(schema)
})
// ** Vars
const hidden = useMediaQuery(theme.breakpoints.down('md'))
const handleSubmit=(e)=>{
e.preventDefault();
fetch(
'url',
{
method:'POST',
body:JSON.stringify({
username:username,
password:password,
}),
headers: {
'Content-Type':'application/json',
'X-Correlation-ID':'123456',
'Source-ID':'abc123',
}
}).then(res =>{
if(res.ok){
return res.json();
}
else{
return res.json().then(data=> {
let errorMessage="Authentication Failed";
throw new Error(errorMessage);
});
}
}).then(data=>{
auth.login(data.response.accessToken);
}).catch(err=>{
alert(err.message);
});
}
return (
<Box className='content-right'>
{!hidden ? (
<Box sx={{ flex: 1, display: 'flex', position: 'relative', alignItems: 'center', justifyContent: 'center' }}>
<LoginIllustrationWrapper>
<LoginIllustration
alt='login-illustration'
src={`/images/pages/auth-v2-login-illustration-${theme.palette.mode}.png`}
/>
</LoginIllustrationWrapper>
<FooterIllustrationsV2 />
</Box>
) : null}
<RightWrapper sx={skin === 'bordered' && !hidden ? { borderLeft: `1px solid ${theme.palette.divider}` } : {}}>
<Box
sx={{
p: 12,
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'background.paper'
}}
>
<BoxWrapper>
<Box
sx={{
top: 30,
left: 40,
display: 'flex',
position: 'absolute',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant='h6'
sx={{
ml: 3,
lineHeight: 1,
fontWeight: 600,
textTransform: 'uppercase',
fontSize: '1.5rem !important'
}}
>
{themeConfig.templateName}
</Typography>
</Box>
<Box sx={{ mb: 6 }}>
<TypographyStyled variant='h5'>Welcome to {themeConfig.templateName}! 👋🏻</TypographyStyled>
<Typography variant='body2'>Please sign-in to your account and start the adventure</Typography>
</Box>
<form noValidate autoComplete='off' onSubmit={handleSubmit}>
<FormControl fullWidth sx={{ mb: 4 }}>
<Controller
name='Username'
control={control}
rules={{ required: true }}
render={({ field: { value, onChange, onBlur } }) => (
<TextField
autoFocus
label='Username'
value={value}
onBlur={onBlur}
//onChange={onChange}
onChange={(e)=> setUsername(e.target.value)}
error={Boolean(errors.user)}
// placeholder='admin#materio.com'
/>
)}
/>
{errors.user && <FormHelperText sx={{ color: 'error.main' }}>{errors.user.message}</FormHelperText>}
</FormControl>
<FormControl fullWidth>
<InputLabel htmlFor='auth-login-v2-password' error={Boolean(errors.password)}>
Password
</InputLabel>
<Controller
name='password'
control={control}
rules={{ required: true }}
render={({ field: { value,onChange, onBlur } }) => (
<OutlinedInput
value={value}
onBlur={onBlur}
label='Password'
//onChange={onChange}
onChange={(e)=> setPassword(e.target.value)}
id='auth-login-v2-password'
error={Boolean(errors.password)}
type={showPassword ? 'text' : 'password'}
endAdornment={
<InputAdornment position='end'>
<IconButton
edge='end'
onMouseDown={e => e.preventDefault()}
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? <EyeOutline /> : <EyeOffOutline />}
</IconButton>
</InputAdornment>
}
/>
)}
/>
{errors.password && (
<FormHelperText sx={{ color: 'error.main' }} id=''>
{errors.password.message}
</FormHelperText>
)}
</FormControl>
<Box
sx={{ mb: 4, display: 'flex', alignItems: 'center', flexWrap: 'wrap', justifyContent: 'space-between' }}
>
<FormControlLabel control={<Checkbox />} label='Remember Me' />
<Link passHref href='/forgot-password'>
<LinkStyled>Forgot Password?</LinkStyled>
</Link>
</Box>
<Button fullWidth size='large' type='submit' variant='contained' sx={{ marginBottom: 7 }}>
Login
</Button>
<Box sx={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', justifyContent: 'center' }}>
<Typography variant='body2' sx={{ marginRight: 2 }}>
New on our platform?
</Typography>
<Typography variant='body2'>
<Link passHref href='/register'>
<LinkStyled>Create an account</LinkStyled>
</Link>
</Typography>
</Box>
<Divider sx={{ my: 5 }}>or</Divider>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Link href='/' passHref>
<IconButton component='a' onClick={e => e.preventDefault()}>
<Facebook sx={{ color: '#497ce2' }} />
</IconButton>
</Link>
<Link href='/' passHref>
<IconButton component='a' onClick={e => e.preventDefault()}>
<Twitter sx={{ color: '#1da1f2' }} />
</IconButton>
</Link>
<Link href='/' passHref>
<IconButton component='a' onClick={e => e.preventDefault()}>
<Github
sx={{ color: theme => (theme.palette.mode === 'light' ? '#272727' : theme.palette.grey[300]) }}
/>
</IconButton>
</Link>
<Link href='/' passHref>
<IconButton component='a' onClick={e => e.preventDefault()}>
<Google sx={{ color: '#db4437' }} />
</IconButton>
</Link>
</Box>
</form>
</BoxWrapper>
</Box>
</RightWrapper>
</Box>
)
}
LoginPage.getLayout = page => <BlankLayout>{page}</BlankLayout>
LoginPage.guestGuard = true
export default LoginPage
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;
}
};
I have written the following website:https://konekto-hgol6b5mz.now.sh
If you quickly click through the onboarding and settings you can see that I have one screen with on "Direct SOS" button. However, its function is not executed and thus I do not get redirected. Here you can see the class concerning that:
import React from 'react';
import { Button, Grid } from '#material-ui/core';
import { withStyles } from '#material-ui/core/styles';
import AppContext from '../utils/AppContext';
const styles = theme => ({
container: {
alignItems: 'center',
// background: 'white',
border: 'black',
'border-width': 'medium',
'margin-top': '80px',
background: 'rgba(255, 255, 255, 0.8)',
'border-radius': '20px'
},
item: {
// background: 'red',
width: '100%',
//background: 'white',
'text-align': 'center',
'border-radius': '5px',
'margin-top': '10px'
},
sosbutton: {
background: 'red',
'text-align': 'center',
'margin-top': '30px',
height: '80%',
width: '100%'
}
});
class Landingpage extends React.Component {
static contextType = AppContext;
constructor(props) {
super(props);
this.classes = props.classes;
this.state = {};
this.handleDirectSOS = this.handleDirectSOS.bind(this);
}
componentDidMount() {
console.log(this.context);
if (this.context.onBoardingStatus === false) {
console.log('IN IF');
this.props.history.push('/onboarding');
}
}
handleDirectSOS() {
console.log('direct SOS');
this.props.history.push('/emergency_sent');
}
render() {
console.log('direct SOS');
return (
<Header title="Send out SOS" />
<Grid
container
className={this.classes.container}
direction="column"
spacing={2}
>
<Grid
item
sm={12}
className={(this.classes.item, this.classes.forwardbutton)}
>
<Button
className={this.classes.sosbutton}
name="type_person"
value="1"
onClick={this.props.handleDirectSOS}
>
Direct SOS
</Button>
</Grid>
</Grid>
);
}
}
export default withStyles(styles)(Landingpage);
index.js:1375 Warning: Can't perform a React state update on an
unmounted component. This is a no-op, but it indicates a memory leak
in your application. To fix, cancel all subscriptions and asynchronous
tasks in the componentWillUnmount method.
in Settings (created by WithStyles(Settings))
in WithStyles(Settings) (created by Context.Consumer)
This might be associated with the Settings file but I have no idea what might be the reason. Here you can see the settings file:
import React from 'react';
import axios from 'axios';
import {
Grid,
Box,
Container,
Typography,
Button,
TextField
} from '#material-ui/core';
import { withStyles } from '#material-ui/core/styles';
import Header from '../Layout/Header';
import CONST from '../utils/Constants';
const CssTextField = withStyles({
root: {
'& label.Mui-focused': {
color: 'green'
},
'& .MuiInput-underline:after': {
borderBottomColor: 'green'
},
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: 'red'
},
'&:hover fieldset': {
borderColor: 'yellow'
},
'&.Mui-focused fieldset': {
borderColor: 'green'
}
}
},
layout: {
width: '100%'
}
})(TextField);
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
width: '100%'
},
title: {
'text-align': 'center'
},
textfield: {
'margin-top': theme.spacing(1),
'margin-bottom': theme.spacing(2)
}
});
//const classes = useStyles();
class Settings extends React.Component {
constructor(props) {
super(props);
//const { classes } = props;
this.classes = props.classes;
this.state = {};
this.onChangeText = this.onChangeText.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChangeText(e) {
console.log('text has changed.');
const key = e.target.id;
const value = e.target.value;
let state_obj = {};
state_obj[key] = value;
this.setState(state_obj);
}
onSubmit(e) {
console.log('Submit button pressed.');
axios
.post(CONST.URL + 'user/update', {
id: 1,
data: this.state
})
.then(res => {
console.log(res);
console.log(res.data);
})
.catch(err => {
console.log(err);
});
this.props.history.push('/');
}
componentDidMount() {
console.log('Component did mount.');
axios
.get(CONST.URL + 'user', {
params: { id: 1 }
})
.then(resp => {
// console.log(resp);
const data = resp.data.data;
this.setState(data);
console.log(this.state.fullname);
})
.catch(function(error) {
console.log(error);
})
.then(function() {
// always executed
});
}
render() {
return (
<React.Fragment>
<Header title="Settings" BackButton="true" />
<Container component="main" maxWidth="sm">
{/* <Typography variant="h4" align="center" gutterBottom="true">
Settings
</Typography> */}
<Box className={this.classes.textfield}>
<Grid
container
direction="column"
justify="flex-end"
alignItems="left"
item
xs
>
<Typography variant="h6">Personal Information</Typography>
<CssTextField
id="fullname"
label="Fullname"
onChange={this.onChangeText}
value={this.state.fullname}
/>
<CssTextField
id="birthday"
label="Birthday"
onChange={this.onChangeText}
value={this.state.birthday}
/>
<CssTextField
id="address"
label="Home address"
onChange={this.onChangeText}
value={this.state.address}
/>
</Grid>
</Box>
<Box className={this.classes.textfield}>
<Grid
container
direction="column"
justify="flex-end"
alignItems="left"
item
xs
>
<Typography variant="h6">Health information</Typography>
<CssTextField
id="allergies"
label="Allergies"
onChange={this.onChangeText}
value={this.state.allergies}
/>
<CssTextField
id="past_injuries"
label="Past injuries"
onChange={this.onChangeText}
value={this.state.past_injuries}
/>
</Grid>
</Box>
<Box className={this.classes.textfield}>
<Grid
container
direction="column"
justify="flex-end"
alignItems="left"
item
xs
>
<Typography variant="h6">Contact information</Typography>
<CssTextField
id="fullname_relative_1"
label="Fullname relative 1"
onChange={this.onChangeText}
value={this.state.fullname_relative_1}
/>
<CssTextField
id="phone_number_relative_1"
label="Phone number relative 1"
onChange={this.onChangeText}
value={this.state.phone_number_relative_1}
/>
<CssTextField
id="fullname_relative_2"
label="Fullname relative 2"
onChange={this.onChangeText}
value={this.state.fullname_relative_2}
/>
<CssTextField
id="phone_number_relative_2"
label="Phone number relative 2"
onChange={this.onChangeText}
value={this.state.phone_number_relative_2}
/>
</Grid>
</Box>
<Box>
<Grid
container
direction="column"
justify="flex-end"
alignItems="left"
item
xs
>
<Button
variant="contained"
className={this.classes.button}
onClick={this.onSubmit}
>
Save
</Button>
<br />
{/* <Button
variant="contained"
className={this.classes.button}
onClick={() => {
this.props.history.push('/');
}}
>
Cancel emergency
</Button> */}
{/* <br /> */}
<Button
variant="contained"
className={this.classes.button}
onClick={() => {
this.props.history.push('/onboarding_reset');
}}
>
Reset App
</Button>
{/* <br />
<Button
variant="contained"
className={this.classes.button}
onClick={() => {
this.props.history.push('/Signin');
}}
>
Signin
</Button> */}
</Grid>
</Box>
</Container>
</React.Fragment>
);
}
}
export default withStyles(styles)(Settings);
I would really appreciate your help!
I think you have two separate issues.
The button press issue is because you're calling onClick={this.props.handleDirectSOS} instead of onClick={this.handleDirectSOS}
The error you're seeing is because of the line this.setState(data);, you need to wrap that or cancel the call if the component is unmounted. There's quite a few articles on it like Is there a way to check if the react component is unmounted?
I have created a user register page on the react-admin template, but when it's rendered, the sidebar and appbar are being rendered as well.
The default login page in the template gets rendered without these 2 components.
How do I configure the app so that the register page also gets rendered without the sidebar and appbar?
MyLayout.js
// in src/MyLayout.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withStyles, MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import {
AppBar,
Menu,
Notification,
Sidebar,
setSidebarVisibility,
} from 'react-admin';
const styles = theme => ({
root: {
display: 'flex',
flexDirection: 'column',
zIndex: 1,
minHeight: '100vh',
backgroundColor: theme.palette.background.default,
position: 'relative',
},
appFrame: {
display: 'flex',
flexDirection: 'column',
overflowX: 'auto',
},
contentWithSidebar: {
display: 'flex',
flexGrow: 1,
},
content: {
display: 'flex',
flexDirection: 'column',
flexGrow: 2,
padding: theme.spacing.unit * 3,
marginTop: '4em',
paddingLeft: 5,
},
});
class MyLayout extends Component {
componentWillMount() {
this.props.setSidebarVisibility(true);
}
render() {
const {
children,
classes,
dashboard,
isLoading,
logout,
open,
title,
} = this.props;
return (
<div className={classes.root}>
<div className={classes.appFrame}>
<AppBar title={title} open={open} logout={logout} color="primary"/>
<main className={classes.contentWithSidebar}>
<Sidebar>
<Menu logout={logout} hasDashboard={!!dashboard} />
</Sidebar>
<div className={classes.content}>
{children}
</div>
</main>
<Notification />
</div>
</div>
);
}
}
MyLayout.propTypes = {
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
dashboard: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string,
]),
isLoading: PropTypes.bool.isRequired,
logout: 'componentPropType',
setSidebarVisibility: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
};
const mapStateToProps = state => ({ isLoading: state.admin.loading > 0 });
export default connect(mapStateToProps, { setSidebarVisibility })(withStyles(styles)(MyLayout));
register.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { propTypes, reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import Card from '#material-ui/core/Card';
import CardActions from '#material-ui/core/CardActions';
import CircularProgress from '#material-ui/core/CircularProgress';
import TextField from '#material-ui/core/TextField';
import { withStyles } from '#material-ui/core/styles';
import LockIcon from '#material-ui/icons/Lock';
import { Notification, translate, userLogin } from 'react-admin';
import { Link } from 'react-router-dom';
import dataProvider from './dataProvider';
const styles = theme => ({
main: {
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'flex-start',
background: 'url(https://source.unsplash.com/random/1600x900)',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
},
card: {
minWidth: 300,
marginTop: '6em',
},
avatar: {
margin: '1em',
display: 'flex',
justifyContent: 'center',
},
icon: {
backgroundColor: theme.palette.secondary.main,
},
hint: {
marginTop: '1em',
display: 'flex',
justifyContent: 'center',
color: theme.palette.grey[500],
},
form: {
padding: '0 1em 1em 1em',
},
input: {
marginTop: '1em',
},
actions: {
padding: '0 1em 1em 1em',
},
});
// see http://redux-form.com/6.4.3/examples/material-ui/
const renderInput = ({
meta: { touched, error } = {},
input: { ...inputProps },
...props
}) => (
<TextField
error={!!(touched && error)}
helperText={touched && error}
{...inputProps}
{...props}
fullWidth
/>
);
class Register extends Component {
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
email: '',
password: '',
submitted: false
};
//this.handleSubmit = this.handleSubmit.bind(this);
this.handleFirstNameChange = this.handleFirstNameChange.bind(this);
this.handleLastNameChange = this.handleLastNameChange.bind(this);
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
}
handleFirstNameChange(e){
this.setState({firstName:e.target.value});
}
handleLastNameChange(e) {
this.setState({ lastName: e.target.value });
}
handleEmailChange(e) {
this.setState({ email: e.target.value });
}
handlePasswordChange(e) {
this.setState({ password: e.target.value });
}
register = () => {
this.setState({ submitted: true });
const { firstName, lastName, email, password } = this.state;
//console.log(data);
//console.log(firstName);
dataProvider('CREATE', 'user/register', { 'data': { 'fname': firstName, 'lname': lastName, 'email': email, 'password': password } });
/*dataProvider('CREATE', 'user/register', { data: {
fname: 1, lname: 5, email: test, password: 1234
} });*/
}
render() {
const { classes, handleSubmit, isLoading, translate } = this.props;
return (
<div className={classes.main}>
<Card className={classes.card}>
<div className={classes.avatar}>
<Avatar className={classes.icon}>
<LockIcon />
</Avatar>
</div>
<form onSubmit={handleSubmit(this.register)}>
<div className={classes.hint}></div>
<div className={classes.form}>
<div className={classes.input}>
<Field
name="firstName"
component={renderInput}
label={'First Name'}
disabled={isLoading}
value={this.state.firstName}
onChange={this.handleFirstNameChange}
/>
</div>
<div className={classes.input}>
<Field
name="lastName"
component={renderInput}
label={'Last Name'}
disabled={isLoading}
value={this.state.lastName}
onChange={this.handleLastNameChange}
/>
</div>
<div className={classes.input}>
<Field
name="email"
component={renderInput}
label={'Email'}
disabled={isLoading}
value={this.state.email}
onChange={this.handleEmailChange}
/>
</div>
<div className={classes.input}>
<Field
name="password"
component={renderInput}
label={translate('ra.auth.password')}
type="password"
disabled={isLoading}
value={this.state.password}
onChange={this.handlePasswordChange}
/>
</div>
</div>
<CardActions className={classes.actions}>
<Button
variant="raised"
type="submit"
color="primary"
disabled={isLoading}
className={classes.button}
fullWidth
>
{isLoading && (
<CircularProgress size={25} thickness={2} />
)}
{'Register'}
</Button>
</CardActions>
<CardActions className={classes.actions}>
<Button
variant="raised"
color="secondary"
disabled={isLoading}
className={classes.button}
fullWidth
>
<Link to={{pathname: "/login"}} style={{textDecoration: 'none', color:'#fff'}} >Sign In</Link>
</Button>
</CardActions>
</form>
</Card>
<Notification />
</div>
);
}
}
Register.propTypes = {
...propTypes,
authProvider: PropTypes.func,
classes: PropTypes.object,
previousRoute: PropTypes.string,
translate: PropTypes.func.isRequired,
userLogin: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({ isLoading: state.admin.loading > 0 });
const enhance = compose(
translate,
reduxForm({
form: 'signIn',
validate: (values, props) => {
const errors = {};
const { translate } = props;
if (!values.fname) {
errors.fname = translate('ra.validation.required');
}
if (!values.lname) {
errors.lname = translate('ra.validation.required');
}
if (!values.email) {
errors.email = translate('ra.validation.required');
}
if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email) ) {
errors.email = 'A valid email is required';
}
if (!values.password) {
errors.password = translate('ra.validation.required');
}
return errors;
},
}),
connect(
mapStateToProps,
{ userLogin }
),
withStyles(styles)
);
export default enhance(Register);
I believe you will use a customRoute to add your register page to router. You can add a 'noLayout' for not displaying appbar and sidebar
https://github.com/marmelab/react-admin/blob/master/docs/Admin.md#customroutes