Conditional Redirect is not working in react-router-dom - reactjs

I am using React as Front-End and Firebase for Backend and Database. I login.js component, when I signed in, I expect to redirect it to some formfield component. But, here when I try to Redirect the user after Success message from firebase, Redirection does not work conditionally. I used condition Operator for
redirection. Here's the core for Login.js
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import { BrowserRouter as Router, Redirect} from 'react-router-dom';
import { Button, Card, CardContent, Paper, Typography, TextField, Tabs, Tab,FormControl } from '#material-ui/core';
import DateFnsUtils from '#date-io/date-fns';
import {
MuiPickersUtilsProvider,
KeyboardDatePicker,
} from '#material-ui/pickers';
import DataField from './DataField';
import * as firebase from 'firebase';
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
margin: 'auto',
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
},
cards: {
maxWidth: 300,
margin: 'auto',
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
},
formControl: {
padding: theme.spacing(2),
margin: 'auto',
display: 'flex',
flexDirection: 'column',
justigyContent: 'space-around'
},
field: {
minWidth: 250
}
}));
function Login(){
const classes = useStyles();
const [email, setMail] = React.useState();
const [pass, setPass] = React.useState(new Date( '1995-08-18T21:11:54' ));
const auth = firebase.auth();
const handleDateChange = Date => {
const values = Date.toString();
setPass(values)
}
const handlerMail = e => {
setMail(e.target.value);
}
const verify = () => {
const promise = auth.signInWithEmailAndPassword(email, pass);
promise.catch(e => console.log(e.message));
firebase.auth().onAuthStateChanged((user) => {
return ( user ? <Redirect from='/' to='/datafield' /> : <Redirect from='/' to='/' /> )
})
}
return(
<React.Fragment className={classes.root}>
<Card className={classes.cards} elevation={0}>
<Typography variant='h5' color='primary'>Login</Typography>
<CardContent>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<FormControl className={classes.formControl}>
<TextField id="standard-basic" value={email} onChange={handlerMail} className={classes.field} label="Email" />
<KeyboardDatePicker
margin="normal"
id="date-picker-dialog"
label="Date of Birth"
format="MM/dd/yyyy"
value={pass}
onChange={handleDateChange}
KeyboardButtonProps={{
'aria-label': 'change date',
}}
/>
</FormControl>
</MuiPickersUtilsProvider>
</CardContent>
<Button color='third' onClick={verify} variant='contained'>Login</Button>
</Card>
</React.Fragment>
)
};
export default Login;
I've been working for couples of days to solve it! Not yet found!!!
Can anybody help me to resolve this issue? Thank You!

Try histroy.replace, doc: https://reacttraining.com/react-router/web/api/history
Wrap Login component in withRouter HOC. export default withRouter(Login);
const verify = () => {
const { history } = props;
const promise = auth.signInWithEmailAndPassword(email, pass);
promise.catch(e => console.log(e.message));
firebase.auth().onAuthStateChanged(user => {
if (user) {
history.replace("/datafield");
}
});
};

Related

I want to navigate to home page upon successfull authentication

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

Materail UI TypeError: event.target.getAttribute is not a function

I am making a form in React using Material UI and displaying the form data in a table on client side, the problem is when I Select language by the select dropdown then I get an error of TypeError: event.target.getAttribute is not a function.
Although This language data is not displayed in the table but I need it, it needs to be stored in the database(which currently I have not implemented)
I have referred Material UI select throws event.target.getAttribute is not a function and also this github issue but it did not worked for me.
Currently I am not using any state management tool like redux, I am passing down props from my App component.
Please help me as in where am I going wrong
App.js
import Projects from './components/Projects';
import FormModal from './components/FormModal'
import Sidebar from './components/Sidebar';
import { useState } from 'react';
import {nanoid} from 'nanoid'
import DeleteModal from './components/DeleteModal';
function App() {
const [formDatas,setFormDatas] = useState(
[
{
projectName: 'Test',
projectId: nanoid(),
nameSpace: 'John Doe'
},
])
// Show Table State
const [showTable, setShowTable] = useState(false)
// HOOKS FORM MODAL
const [addFormData, setAddFormData] = useState({
projectName: '',
projectDescription: '',
nameSpace: '',
language: ''
})
const handleAddFormChange = (event) => {
event.preventDefault()
const fieldName = event.target.getAttribute('name')
console.log(fieldName)
const fieldValue = event.target.value
const newFormData = {...addFormData}
newFormData[fieldName] = fieldValue
setAddFormData(newFormData)
}
// FORM SUBMIT HANDLER
const handleAddFormSubmit = (event) => {
event.preventDefault()
const newProject = {
projectId: nanoid(),
projectName: addFormData.projectName,
projectDescription: addFormData.projectDescription,
nameSpace: addFormData.nameSpace,
language: addFormData.language
}
// Copy all the previous formDatas and add newProject Data
const newProjects = [...formDatas, newProject ]
setFormDatas(newProjects)
// console.log(newProject)
// Clear Form Input Fields after Submitting
setAddFormData({
projectName: '',
projectDescription: '',
nameSpace: '',
language: ''
})
// After Submitting close Form Modal
handleClose()
}
// Delete Handler
const deleteModalHandler = (addFormData) => {
console.log(addFormData.projectId)
}
// MODAL OPEN CLOSE STATE
const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<>
<Sidebar showTable = {showTable} setShowTable = {setShowTable}/>
<FormModal addFormData = {addFormData}
handleAddFormChange = {handleAddFormChange}
handleAddFormSubmit = {handleAddFormSubmit}
open = {open}
handleOpen = {handleOpen}
handleClose = {handleClose}/>
<Projects
formDatas = {formDatas}
addFormData = {addFormData}
deleteModalHandler={deleteModalHandler}
showTable = {showTable}
setShowTable = {setShowTable}/>
</>
);
}
export default App;
FormModal.js
import * as React from 'react';
import Box from '#mui/material/Box';
import Button from '#mui/material/Button';
import { TextField } from "#mui/material";
import Modal from '#mui/material/Modal';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import Select from '#mui/material/Select';
import { useState } from 'react';
import SearchIcon from '#mui/icons-material/Search';
import { InputAdornment } from '#mui/material';
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
export default function FormModal({handleAddFormChange, addFormData,handleAddFormSubmit,open,handleOpen,handleClose}) {
return (
<div>
<Box sx = {{display: 'flex', justifyContent: 'flex-end',alignItems: 'flex-end', flexDirection: 'column'}}>
<Button sx = {{ marginTop: '5%', marginRight: '4%', borderRadius: '10px '}} variant="contained" onClick={handleOpen}>Create project + </Button>
{/* <input style = {{marginTop: '2.5%', marginRight: '5%', padding: '5px'}} type="text" placeholder = "Search..." /> */}
<TextField sx = {{marginTop: '2%', marginRight: '4%', padding: '5px'}} placeholder = "Search..." InputProps={{
endAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
)
}}/>
</Box>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<div style = {{height: '5vh', display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
<h3>Create New Project</h3>
</div>
<form onSubmit = {handleAddFormSubmit}>
<TextField
id="outlined-basic"
label="Project Name"
name = "projectName"
variant="outlined"
fullWidth= {true}
required = {true}
margin ="normal"
value={addFormData.projectName}
onChange={handleAddFormChange}
/>
<TextField
id="outlined-basic"
label="Project Description"
name = "projectDescription"
variant="outlined"
fullWidth = {true}
required = {true}
multiline
rows = {5}
margin ="normal"
value={addFormData.projectDescription}
onChange={handleAddFormChange}
/>
<TextField
id="outlined-basic"
label="Name Space"
name = "nameSpace"
variant="outlined"
fullWidth= {true}
required = {true}
autoComplete = {Math.random().toString()}
margin = "normal"
value={addFormData.nameSpace}
onChange={handleAddFormChange}
/>
<InputLabel id="demo-simple-select-label">Select Language</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
name = "name"
value={addFormData.language}
onChange={handleAddFormChange}
style = {{width: '100%'}}
>
<MenuItem value="english">English</MenuItem>
</Select>
<br />
<div style = {{display: 'flex', alignItems: 'center', justifyContent: 'space-evenly', marginTop: '7%'}}>
<Button style = {{width: '40%'}} variant = "contained" type="submit">Submit</Button>
<Button style = {{width: '40%'}} variant = "contained" onClick = {handleClose}>Cancel</Button>
</div>
</form>
</Box>
</Modal>
</div>
);
}
Projects.js
import React from "react";
import EditModal from "./EditModal";
import DeleteModal from "./DeleteModal";
import { styled } from "#mui/material/styles";
import Table from "#mui/material/Table";
import TableBody from "#mui/material/TableBody";
import TableCell, { tableCellClasses } from "#mui/material/TableCell";
import TableContainer from "#mui/material/TableContainer";
import TableHead from "#mui/material/TableHead";
import TableRow from "#mui/material/TableRow";
import { useState } from "react";
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
// backgroundColor: theme.palette.common.black,
backgroundColor: "#1976D2",
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
// hide last border
"&:last-child td, &:last-child th": {
border: 0,
},
}));
const Projects = ({ showTable, setShowTable,addFormData, formDatas,deleteModalHandler}) => {
//const [showTable, setShowTable] = useState(true
return (
<div style={{ display: "flex", alignItems: "center", marginTop: "7%" }}>
{showTable ? <TableContainer > {/*component={Paper}*/}
<Table sx={{ width: '50%', marginLeft: 'auto', marginRight: 'auto' }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell align="left">Project Name</StyledTableCell>
<StyledTableCell align="left">Project Id</StyledTableCell>
<StyledTableCell align="left">Created By</StyledTableCell>
<StyledTableCell align="center">Operations</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{formDatas.map((formData) => (
<StyledTableRow key = {formData.projectId}>
<StyledTableCell align="left" component="th" scope="row">
{formData.projectName}
</StyledTableCell>
<StyledTableCell align="left">
{formData.projectId}
</StyledTableCell>
<StyledTableCell align="left">
{formData.nameSpace}
</StyledTableCell>
<StyledTableCell align="center">
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-evenly",
}}
>
<EditModal />
<DeleteModal deleteModalHandler = {deleteModalHandler}/>
</div>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>: null}
</div>
);
};
export default Projects;
As mentioned #Pradip Dhakal in the comment <Select> only return the {name: '', value: ''} as a target object, this is causing the issue. There is no other objects.
So I used Material UI Native Select Component and it solved the problem
The only code that I changed in FormModal.js file is below.
<NativeSelect
// defaultValue={30}
value={addFormData.language}
onChange={handleAddFormChange}
inputProps={{
name: 'language',
id: 'uncontrolled-native',
}}
>
<option value = "" disabled>Select</option>
<option value={'english'}>English</option>
<option value={'german'}>German</option>
</NativeSelect>
This is the minimal code that I've try, and it's working fine in my end.
material UI version: "#material-ui/core": "^4.11.0",
// FormModal.js
import React from 'react';
import { TextField } from "#material-ui/core"
export const FormModal = ({ handleAddFormChange }) => {
return (
<TextField
id="outlined-basic"
label="Project Name"
name="projectName"
variant="outlined"
fullWidth={true}
required={true}
margin="normal"
value=""
onChange={handleAddFormChange}
/>
);
}
// app.js
import React from 'react';
import { TextField } from "#material-ui/core"
import { FormModal } from "./FormModal"
const App = () => {
const handleAddFormChange = (event) => {
event.preventDefault()
const fieldName = event.target.getAttribute('name')
console.log("Name: ", fieldName)
}
return (
<FormModal handleAddFormChange={handleAddFormChange} />
);
}
Can you please try to breakdown your code, and start from the basic one.

useEffect running on mount causing error and blank object

I keep getting a blank avatarlistitem when I click add for the first time and then after that the appropriate item will display after the second click. If I add a new URL and click the third time the item won't appear until the fourth click. I am most likely doing something wrong with my useEffect and state values.
import React, { useState, useEffect } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import AvatarListItem from './AvatarListItem';
import AddIcon from '#material-ui/icons/Add';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import axios from 'axios';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'space-between',
flexDirection: 'column',
flexWrap: 'wrap',
backgroundColor: theme.palette.background.paper,
},
inline: {
display: 'inline',
},
formControl: {
width: '100%',
margin: theme.spacing(1),
marginBottom: '50px',
minWidth: 120,
},
extendedIcon: {
margin: '10px',
marginRight: theme.spacing(1),
},
}));
export default function AvatarList() {
const classes = useStyles();
const [url, setUrl] = useState('');
const [itemDetails, setItemDetails] = useState({});
const [items, setItems] = useState([]);
const [search, setSearch] = useState('');
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`http://127.0.0.1:5000/api/resources/products?url=${url}`,
)
setItemDetails(result.data[0]);
}
fetchData()
}, [search])
const addItem = (itemDetails) => {
const newItems = [...items, itemDetails];
setItems(newItems);
};
let handleSubmit = (e) => {
e.preventDefault();
console.log(itemDetails);
addItem(itemDetails);
};
let removeItem = (index) => {
const newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
return (
<div>
<form className={classes.formControl} onSubmit={e => handleSubmit(e)}>
<TextField id="outlined-basic" label="Amazon Url" variant="outlined" name='newItem' onChange={e => setUrl(e.target.value)} value={url} />
<Button variant="contained" color="primary" type="submit" value="Submit" onClick={() => setSearch(url)}>
<AddIcon className={classes.extendedIcon} />
</Button>
</form>
<List className={classes.root}>
{items.map((item, index) => (
<>
<AvatarListItem
itemDetails={item}
key={index}
index={index}
removeItem={removeItem}
/>
<Divider variant="middle" component="li" />
</>
))}
</List>
</div >
);
}
A better approach may be to purely rely on the onSubmit callback instead of relying on the useEffect which may run more often than needed. Also, it doesn't look like you need to use the search or itemDetails state at all.
import React, { useState, useEffect } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import AvatarListItem from './AvatarListItem';
import AddIcon from '#material-ui/icons/Add';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import axios from 'axios';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'space-between',
flexDirection: 'column',
flexWrap: 'wrap',
backgroundColor: theme.palette.background.paper,
},
inline: {
display: 'inline',
},
formControl: {
width: '100%',
margin: theme.spacing(1),
marginBottom: '50px',
minWidth: 120,
},
extendedIcon: {
margin: '10px',
marginRight: theme.spacing(1),
},
}));
export default function AvatarList() {
const classes = useStyles();
const [url, setUrl] = useState('');
const [items, setItems] = useState([]);
const addItem = (itemDetails) => {
const newItems = [...items, itemDetails];
setItems(newItems);
};
let handleSubmit = async (e) => {
e.preventDefault();
const result = await axios(
`http://127.0.0.1:5000/api/resources/products?url=${url}`,
)
addItem(result.data[0]);
};
let removeItem = (index) => {
const newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
return (
<div>
<form className={classes.formControl} onSubmit={handleSubmit}>
<TextField id="outlined-basic" label="Amazon Url" variant="outlined" name='newItem' onChange={e => setUrl(e.target.value)} value={url} />
<Button variant="contained" color="primary" type="submit">
<AddIcon className={classes.extendedIcon} />
</Button>
</form>
<List className={classes.root}>
{items.map((item, index) => (
<React.Fragment key={index}>
<AvatarListItem
itemDetails={item}
key={index}
index={index}
removeItem={removeItem}
/>
<Divider variant="middle" component="li" />
</React.Fragment>
))}
</List>
</div >
);
}
if your intention is fetching data every time user click submit, based on your useEffect, you should not base your useEffect on search but on items itself (as when user submit, you add something to items)
your code should look like this
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`http://127.0.0.1:5000/api/resources/products?url=${url}`,
)
setItemDetails(result.data[0]);
}
fetchData()
}, [items])

Next js: Error: Objects are not valid as a React child (found: Error: Response not successful: Received status code 401)

This app shows Github issues with graphql API.
I didn't change anything after finishing the app but I got this error.
I used Next js, Typescript, Material UI, Tailwind css and GraphQL for this project.
Index Component
import React, { useState } from "react"
import { Typography, Container, makeStyles } from "#material-ui/core"
import SearchBar from "../components/SearchBar/SearchBar"
import RepositoryList from "../components/RepositoryList/RepositoryList"
import Head from "next/head"
const useStyles = makeStyles({
title: {
marginTop: "1rem",
marginBottom: "1rem",
textAlign: "center",
},
})
const App = () => {
const classes = useStyles()
const [searchTerm, setSearchTerm] = useState<string>("")
return (
<>
<Head>
<title>GraphQL Github Client</title>
</Head>
<Container maxWidth={"sm"}>
<div className="mt-10 mb-5">
<Typography variant={"h3"} className={classes.title}>
GraphQL Github Client
</Typography>
</div>
<SearchBar
className="mb-10"
value={searchTerm}
onChange={setSearchTerm}
/>
<RepositoryList searchTerm={searchTerm} />
</Container>
</>
)
}
export default App
RepositoryList Component
import React, { useEffect, useState } from "react"
import { Typography, CircularProgress, makeStyles } from "#material-ui/core"
import { useQuery } from "#apollo/react-hooks"
import { SEARCH_FOR_REPOS } from "../../Queries/queries"
import Repository from "../Repository/Repository"
interface RepositoryListProps {
searchTerm?: string
}
const useStyles = makeStyles({
note: {
marginTop: "1rem",
textAlign: "center",
},
spinnerContainer: {
display: "flex",
justifyContent: "space-around",
marginTop: "1rem",
},
})
const RepositoryList: React.FC<RepositoryListProps> = ({ searchTerm }) => {
const classes = useStyles()
const [expandedRepo, setExpandedRepo] = useState(null)
const { data, loading, error } = useQuery(SEARCH_FOR_REPOS, {
variables: { search_term: searchTerm },
})
useEffect(() => {
setExpandedRepo(null)
}, [data])
if (loading) {
return (
<div className={classes.spinnerContainer}>
<CircularProgress />
</div>
)
}
if (error) {
return (
<Typography
variant={"overline"}
className={classes.note}
component={"div"}
color={"error"}
>
{error}
</Typography>
)
}
if (!data.search.repositoryCount) {
return (
<Typography
variant={"overline"}
className={classes.note}
component={"div"}
>
There are no such repositories!
</Typography>
)
}
return (
<div>
{data.search.edges.map(
(
repo: { edges: { id: number } },
i: string | number | ((prevState: null) => null) | null | any
) => (
<>
<Repository
repo={repo}
expanded={expandedRepo === i}
onToggled={() => setExpandedRepo(i)}
key={repo.edges.id}
/>
</>
)
)}
</div>
)
}
export default RepositoryList
Repository Component
import React from "react"
import {
ExpansionPanel,
ExpansionPanelSummary,
ExpansionPanelDetails,
Typography,
Chip,
makeStyles,
} from "#material-ui/core"
import StarIcon from "#material-ui/icons/Star"
import PeopleIcon from "#material-ui/icons/People"
import IssueList from "../IssueList/IssueList"
const useStyles = makeStyles({
root: {
marginTop: "1rem",
},
summaryContainer: {
flexDirection: "column",
},
summaryHeader: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginBottom: "1rem",
},
chip: {
marginLeft: "0.5rem",
},
})
interface RepositoryProps {
repo: any
expanded: boolean
onToggled: any
}
const Repository: React.FC<RepositoryProps> = ({
repo,
expanded,
onToggled,
}) => {
const {
node: {
name,
descriptionHTML,
owner: { login },
stargazers: { totalCount: totalStarCount },
},
} = repo
const classes = useStyles()
return (
<ExpansionPanel
expanded={expanded}
onChange={onToggled}
className={classes.root}
>
<ExpansionPanelSummary classes={{ content: classes.summaryContainer }}>
<div className={classes.summaryHeader}>
<Typography variant={"h6"}>{name}</Typography>
<div>
<Chip
label={`by ${login}`}
avatar={<PeopleIcon />}
className={classes.chip}
/>
<Chip
label={totalStarCount}
avatar={<StarIcon />}
className={classes.chip}
/>
</div>
</div>
<Typography
variant={"caption"}
dangerouslySetInnerHTML={{ __html: descriptionHTML }}
component={"div"}
/>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
{expanded && <IssueList repoName={name} repoOwner={login} />}
</ExpansionPanelDetails>
</ExpansionPanel>
)
}
export default Repository
These are my components.
What should I do for fixing this problem?
It looks like the issue is in this spot where you do {error}. I would double check what error actually is but it looks like its an object and not a string like you are using it
<Typography
variant={"overline"}
className={classes.note}
component={"div"}
color={"error"}
>
{error}
</Typography>

Login mutation not working consistently in material-ui.

I am trying to create a sample login form in React using material-ui through graphql mutation. But the login form is not working as expected.
i am able to log in only some times, but even in those times, I do not see any data being passed via loginMutation props in console. Could some one please tell me what am i doing wrong here?
Here is the Login Component that I am trying to create
import React, { Component } from 'react';
import { AUTH_TOKEN } from '../constants';
import { graphql, compose } from 'react-apollo';
import {LOGIN_MUTATION} from "../gql/loginGQL";
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField'
import PropTypes from 'prop-types';
import Avatar from '#material-ui/core/Avatar';
import CssBaseline from '#material-ui/core/CssBaseline';
import FormControl from '#material-ui/core/FormControl';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
//import Input from '#material-ui/core/Input';
//import InputLabel from '#material-ui/core/InputLabel';
import LockIcon from '#material-ui/icons/LockOutlined';
import Typography from '#material-ui/core/Typography';
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
layout: {
width: 'auto',
display: 'block', // Fix IE11 issue.
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE11 issue.
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
});
class Login extends Component {
state = {
email: '',
password: '',
errors: null
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<main className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockIcon />
</Avatar>
<Typography variant="headline">Sign in</Typography>
<form className={classes.form}>
<FormControl margin="normal" required fullWidth>
<TextField
id='email'
value={this.state.email}
onChange={e => this.setState({ email: e.target.value })}
type='text'
label='Your email address'
/>
</FormControl>
<FormControl margin="normal" required fullWidth>
<TextField
id='password'
value={this.state.password}
onChange={e => this.setState({ password: e.target.value })}
type='password'
label='Password'
/>
</FormControl>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
id="submit"
type="submit"
fullWidth
variant="raised"
color="primary"
className={classes.submit}
onClick={() => this._confirm()}
>
Sign in
</Button>
</form>
</Paper>
</main>
</React.Fragment>
);
}
_confirm = async () => {
const { email, password } = this.state
try{
const result = await this.props.loginMutation({
variables: {
email,
password,
},
});
console.log(result); // Here no data is being displayed !
const { jwt } = result.data.signInUser;
this._saveUserData(jwt);
this.props.history.push(`/`)
} catch(error) {
const errors = error.graphQLErrors.map(error => error.message);
this.setState({ errors });
}
}
_saveUserData = (token) => {
localStorage.setItem(AUTH_TOKEN, token)
}
}
Login.propTypes = {
classes: PropTypes.object.isRequired,
};
export default compose(
graphql(LOGIN_MUTATION, { name: 'loginMutation' }),
withStyles(styles),
)(Login)
The error was being occurred due to the Button tag. Although, it had type="submit", the form was still not being submitted due to which the input data was not being appearing in the "_confirm". I am wondering why, I must have missed something here.
For now, I created a div tag inside the Button and added onClick handler there instead of Button tag.
So, the edited code just have this small change
<Button color="primary" className = {classes.submit} variant="raised" fullWidth>
<div className="test" onClick={() => this._confirm()} >
Sign in
</div>
</Button>
Here is the full source code:
import React, { Component } from 'react';
import { AUTH_TOKEN } from '../constants';
import { graphql, compose } from 'react-apollo';
import {LOGIN_MUTATION} from "../gql/loginGQL";
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField'
import PropTypes from 'prop-types';
import Avatar from '#material-ui/core/Avatar';
import CssBaseline from '#material-ui/core/CssBaseline';
import FormControl from '#material-ui/core/FormControl';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
//import Input from '#material-ui/core/Input';
//import InputLabel from '#material-ui/core/InputLabel';
import LockIcon from '#material-ui/icons/LockOutlined';
import Typography from '#material-ui/core/Typography';
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
layout: {
width: 'auto',
display: 'block', // Fix IE11 issue.
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE11 issue.
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
});
class Login extends Component {
state = {
email: '',
password: '',
errors: null
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<main className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockIcon />
</Avatar>
<Typography variant="headline">Sign in</Typography>
<form className={classes.form}>
<FormControl margin="normal" required fullWidth>
<TextField
id='email'
value={this.state.email}
onChange={e => this.setState({ email: e.target.value })}
type='text'
label='Your email address'
/>
</FormControl>
<FormControl margin="normal" required fullWidth>
<TextField
id='password'
value={this.state.password}
onChange={e => this.setState({ password: e.target.value })}
type='password'
label='Password'
/>
</FormControl>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
// Here is the change
<Button color="primary" className = {classes.submit}
variant="raised"fullWidth >
<div className="test" onClick={() => this._confirm()} >
Sign in
</div>
</Button>
// Change ends here
</form>
</Paper>
</main>
</React.Fragment>
);
}
_confirm = async () => {
const { email, password } = this.state
try{
const result = await this.props.loginMutation({
variables: {
email,
password,
},
});
console.log(result); // Here no data is being displayed !
const { jwt } = result.data.signInUser;
this._saveUserData(jwt);
this.props.history.push(`/`)
} catch(error) {
const errors = error.graphQLErrors.map(error => error.message);
this.setState({ errors });
}
}
_saveUserData = (token) => {
localStorage.setItem(AUTH_TOKEN, token)
}
}
Login.propTypes = {
classes: PropTypes.object.isRequired,
};
export default compose(
graphql(LOGIN_MUTATION, { name: 'loginMutation' }),
withStyles(styles),
)(Login)

Resources