最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Material UI unable to import and using ClickAwayListener - Stack Overflow

programmeradmin3浏览0评论

I am using material-ui for my project and doing the password-changing functionality, this is my root file where I import children ponents:

Personalize.js

import React, { useContext, useState } from 'react';
import Cookies from 'universal-cookie';
import { makeStyles } from '@material-ui/core/styles';
import { withRouter } from 'react-router-dom';
import { Grid } from '@material-ui/core';
import { CurrentUserContext } from '../providers/CurrentUserContextProvider';

import Header from '../ponent/Header';
import Sidebar from '../ponent/Sidebar';
import ChangeProfileImage from '../ponent/Main/PersonalizeMain/ChangeProfileImage';
import ChangePassword from '../ponent/Main/PersonalizeMain/ChangePassword';

const useStyles = makeStyles((theme) => ({
    root: {
        display: 'flex',
    },
    toolbar: {
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        padding: theme.spacing(0, 1),
        marginTop: '65px',
        // necessary for content to be below app bar
        ...theme.mixins.toolbar,
    },
    content: {
        flexGrow: 1,
        padding: theme.spacing(3),
    },
}));

const Personalize = ({ history, location, match }) => {
    const classes = useStyles();
    const cookies = new Cookies();
    const token = cookies.get('token');

    if (token === undefined) {
        history.push('/login');
    }

    const { currentUser } = useContext(CurrentUserContext);
    const { profileImage } = currentUser;
    const [open, setOpen] = useState(false);

    const handleDrawerOpen = () => {
        setOpen(true);
    };

    const handleDrawerClose = () => {
        setOpen(false);
    };

    return (
        <div className={classes.root}>
            <Header open={open} handleDrawerOpen={handleDrawerOpen} />
            <Sidebar open={open} handleDrawerClose={handleDrawerClose} />
            <main className={classes.content}>
                <div className={classes.toolbar}>
                    <Grid container justify="space-evenly" alignItems="center">
                        <Grid item>
                            <ChangeProfileImage profileImage={profileImage} />
                        </Grid>

                        <Grid item>
                            <ChangePassword />
                        </Grid>
                    </Grid>
                </div>
            </main>
        </div>
    );
};

export default withRouter(Personalize);

As you can see I import a child ponent called ChangePassword, here is the code.

ChangePassword.js

import React, { useState } from 'react';
import Cookies from 'universal-cookie';
import { withRouter } from 'react-router-dom';
import { IconButton, TextField, Grid, Snackbar, ClickAwayListener } from '@material-ui/core';

import MuiAlert from '@material-ui/lab/Alert';

import VisibilityIcon from '@material-ui/icons/Visibility';
import VisibilityOffIcon from '@material-ui/icons/VisibilityOff';

import { makeStyles } from '@material-ui/core';

const Alert = (props) => {
    return <MuiAlert elevation={6} variant="filled" {...props} />;
};

const useStyles = makeStyles((theme) => ({
    MuiInputBaseInput: {
        '& .MuiInputBase-input': {
            minWidth: '300px',
        },
    },
    customePositionIconButton: { position: 'relative', top: '-50px', right: '-130px' },
}));

const ChangePassword = ({ history, location, match }) => {
    const classes = useStyles();
    const [passwordVisible, setPasswordVisible] = useState(false);
    const [confirmPasswordVisible, setConfirmPasswordVisible] = useState(false);
    const [error, setError] = useState('');
    const [open, setOpen] = useState(false);

    const [password, setPassword] = useState({
        newPass: '',
        confirmPass: '',
    });

    const cookies = new Cookies();
    const token = cookies.get('token');

    const handleSubmit = (event) => {
        var charCode = typeof event.which == 'number' ? event.which : event.keyCode;
        var { newPass, confirmPass } = password;
        if (charCode === 13) {
            if (confirmPass === '' || password === '') {
                return setError('Emptied Password');
            }

            if (newPass.trim() === confirmPass.trim()) {
                fetch('http://localhost:5000/users/me', {
                    method: 'PATCH',
                    headers: {
                        Authorization: 'Bearer '.concat(token),
                        'content-type': 'application/json; charset=UTF-8',
                    },
                    body: JSON.stringify({ newPass }),
                })
                    .then((response) => {
                        return response.json();
                    })
                    .then((json) => {
                        var { doc } = json;
                        if (doc) {
                            setOpen(true);
                        }
                    })
                    .catch((error) => {
                        console.error(error);
                    });
            } else {
                setError('Passwords Not Matched');
            }
        }
    };

    const handleClose = (event, reason) => {
        if (reason === 'clickaway') {
            return;
        }

        setOpen(false);
    };

    const handleClickAway = () => {
        setError('');
    };

    return (
        <ClickAwayListener onClickAway={handleClickAway}>
            <form noValidate autoComplete="off">
                <Grid container direction="column" justify="center" alignItems="center">
                    <Grid item className={classes.MuiInputBaseInput}>
                        <TextField
                            type={passwordVisible ? 'text' : 'password'}
                            variant="outlined"
                            label="New Password"
                            required
                            onChange={(event) => {
                                setError('');
                                setPassword({ ...password, newPass: event.target.value });
                            }}
                            onKeyDown={handleSubmit}
                            error={error === '' ? false : true}
                            helperText={error}
                        />
                    </Grid>
                    <Grid item>
                        <IconButton
                            className={classes.customePositionIconButton}
                            onClick={() => {
                                setPasswordVisible(!passwordVisible);
                            }}
                        >
                            {passwordVisible ? <VisibilityOffIcon /> : <VisibilityIcon />}
                        </IconButton>
                    </Grid>

                    <Grid item className={classes.MuiInputBaseInput}>
                        <TextField
                            type={confirmPasswordVisible ? 'text' : 'password'}
                            variant="outlined"
                            label="Confirm Password"
                            required
                            onChange={(event) => {
                                setError('');
                                setPassword({ ...password, confirmPass: event.target.value });
                            }}
                            onKeyDown={handleSubmit}
                            error={error === '' ? false : true}
                            helperText={error}
                        />
                    </Grid>

                    <Grid item>
                        <IconButton
                            className={classes.customePositionIconButton}
                            onClick={() => {
                                setConfirmPasswordVisible(!confirmPasswordVisible);
                            }}
                        >
                            {confirmPasswordVisible ? <VisibilityOffIcon /> : <VisibilityIcon />}
                        </IconButton>
                    </Grid>
                </Grid>
            </form>
            <Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
                <Alert onClose={handleClose} severity="success">
                    Password successfully changed
                </Alert>
            </Snackbar>
        </ClickAwayListener>
    );
};

export default withRouter(ChangePassword);

I have imported ClickAwayListener to setError to an emptied string, but I got this error and I can not understand why. I've already double checked and find no cause.

Element type is invalid: expected a string (for built-in ponents) or a class/function but got: undefined. You likely forgot to export your ponent from the file it's defined in, or you may have mixed up default and named imports

I am using material-ui for my project and doing the password-changing functionality, this is my root file where I import children ponents:

Personalize.js

import React, { useContext, useState } from 'react';
import Cookies from 'universal-cookie';
import { makeStyles } from '@material-ui/core/styles';
import { withRouter } from 'react-router-dom';
import { Grid } from '@material-ui/core';
import { CurrentUserContext } from '../providers/CurrentUserContextProvider';

import Header from '../ponent/Header';
import Sidebar from '../ponent/Sidebar';
import ChangeProfileImage from '../ponent/Main/PersonalizeMain/ChangeProfileImage';
import ChangePassword from '../ponent/Main/PersonalizeMain/ChangePassword';

const useStyles = makeStyles((theme) => ({
    root: {
        display: 'flex',
    },
    toolbar: {
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        padding: theme.spacing(0, 1),
        marginTop: '65px',
        // necessary for content to be below app bar
        ...theme.mixins.toolbar,
    },
    content: {
        flexGrow: 1,
        padding: theme.spacing(3),
    },
}));

const Personalize = ({ history, location, match }) => {
    const classes = useStyles();
    const cookies = new Cookies();
    const token = cookies.get('token');

    if (token === undefined) {
        history.push('/login');
    }

    const { currentUser } = useContext(CurrentUserContext);
    const { profileImage } = currentUser;
    const [open, setOpen] = useState(false);

    const handleDrawerOpen = () => {
        setOpen(true);
    };

    const handleDrawerClose = () => {
        setOpen(false);
    };

    return (
        <div className={classes.root}>
            <Header open={open} handleDrawerOpen={handleDrawerOpen} />
            <Sidebar open={open} handleDrawerClose={handleDrawerClose} />
            <main className={classes.content}>
                <div className={classes.toolbar}>
                    <Grid container justify="space-evenly" alignItems="center">
                        <Grid item>
                            <ChangeProfileImage profileImage={profileImage} />
                        </Grid>

                        <Grid item>
                            <ChangePassword />
                        </Grid>
                    </Grid>
                </div>
            </main>
        </div>
    );
};

export default withRouter(Personalize);

As you can see I import a child ponent called ChangePassword, here is the code.

ChangePassword.js

import React, { useState } from 'react';
import Cookies from 'universal-cookie';
import { withRouter } from 'react-router-dom';
import { IconButton, TextField, Grid, Snackbar, ClickAwayListener } from '@material-ui/core';

import MuiAlert from '@material-ui/lab/Alert';

import VisibilityIcon from '@material-ui/icons/Visibility';
import VisibilityOffIcon from '@material-ui/icons/VisibilityOff';

import { makeStyles } from '@material-ui/core';

const Alert = (props) => {
    return <MuiAlert elevation={6} variant="filled" {...props} />;
};

const useStyles = makeStyles((theme) => ({
    MuiInputBaseInput: {
        '& .MuiInputBase-input': {
            minWidth: '300px',
        },
    },
    customePositionIconButton: { position: 'relative', top: '-50px', right: '-130px' },
}));

const ChangePassword = ({ history, location, match }) => {
    const classes = useStyles();
    const [passwordVisible, setPasswordVisible] = useState(false);
    const [confirmPasswordVisible, setConfirmPasswordVisible] = useState(false);
    const [error, setError] = useState('');
    const [open, setOpen] = useState(false);

    const [password, setPassword] = useState({
        newPass: '',
        confirmPass: '',
    });

    const cookies = new Cookies();
    const token = cookies.get('token');

    const handleSubmit = (event) => {
        var charCode = typeof event.which == 'number' ? event.which : event.keyCode;
        var { newPass, confirmPass } = password;
        if (charCode === 13) {
            if (confirmPass === '' || password === '') {
                return setError('Emptied Password');
            }

            if (newPass.trim() === confirmPass.trim()) {
                fetch('http://localhost:5000/users/me', {
                    method: 'PATCH',
                    headers: {
                        Authorization: 'Bearer '.concat(token),
                        'content-type': 'application/json; charset=UTF-8',
                    },
                    body: JSON.stringify({ newPass }),
                })
                    .then((response) => {
                        return response.json();
                    })
                    .then((json) => {
                        var { doc } = json;
                        if (doc) {
                            setOpen(true);
                        }
                    })
                    .catch((error) => {
                        console.error(error);
                    });
            } else {
                setError('Passwords Not Matched');
            }
        }
    };

    const handleClose = (event, reason) => {
        if (reason === 'clickaway') {
            return;
        }

        setOpen(false);
    };

    const handleClickAway = () => {
        setError('');
    };

    return (
        <ClickAwayListener onClickAway={handleClickAway}>
            <form noValidate autoComplete="off">
                <Grid container direction="column" justify="center" alignItems="center">
                    <Grid item className={classes.MuiInputBaseInput}>
                        <TextField
                            type={passwordVisible ? 'text' : 'password'}
                            variant="outlined"
                            label="New Password"
                            required
                            onChange={(event) => {
                                setError('');
                                setPassword({ ...password, newPass: event.target.value });
                            }}
                            onKeyDown={handleSubmit}
                            error={error === '' ? false : true}
                            helperText={error}
                        />
                    </Grid>
                    <Grid item>
                        <IconButton
                            className={classes.customePositionIconButton}
                            onClick={() => {
                                setPasswordVisible(!passwordVisible);
                            }}
                        >
                            {passwordVisible ? <VisibilityOffIcon /> : <VisibilityIcon />}
                        </IconButton>
                    </Grid>

                    <Grid item className={classes.MuiInputBaseInput}>
                        <TextField
                            type={confirmPasswordVisible ? 'text' : 'password'}
                            variant="outlined"
                            label="Confirm Password"
                            required
                            onChange={(event) => {
                                setError('');
                                setPassword({ ...password, confirmPass: event.target.value });
                            }}
                            onKeyDown={handleSubmit}
                            error={error === '' ? false : true}
                            helperText={error}
                        />
                    </Grid>

                    <Grid item>
                        <IconButton
                            className={classes.customePositionIconButton}
                            onClick={() => {
                                setConfirmPasswordVisible(!confirmPasswordVisible);
                            }}
                        >
                            {confirmPasswordVisible ? <VisibilityOffIcon /> : <VisibilityIcon />}
                        </IconButton>
                    </Grid>
                </Grid>
            </form>
            <Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
                <Alert onClose={handleClose} severity="success">
                    Password successfully changed
                </Alert>
            </Snackbar>
        </ClickAwayListener>
    );
};

export default withRouter(ChangePassword);

I have imported ClickAwayListener to setError to an emptied string, but I got this error and I can not understand why. I've already double checked and find no cause.

Element type is invalid: expected a string (for built-in ponents) or a class/function but got: undefined. You likely forgot to export your ponent from the file it's defined in, or you may have mixed up default and named imports

Share Improve this question edited Oct 4, 2020 at 10:21 NearHuscarl 82.2k24 gold badges320 silver badges283 bronze badges asked Oct 4, 2020 at 6:55 Tam DoTam Do 3211 gold badge6 silver badges20 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 5

ClickAwayListener only accepts one child ponent, not array of ponents. So in your code, change this

<ClickAwayListener onClickAway={handleClickAway}>
  <form>
    {...}
  </form>
  <Snackbar />
</ClickAwayListener>

To this

<>
  <ClickAwayListener onClickAway={handleClickAway}>
    <form>
      {...}
    </form>
  </ClickAwayListener>
  <Snackbar />
</>

And it will work again.

发布评论

评论列表(0)

  1. 暂无评论