描述:
我正在构建一个使用 Material-UI、react-router-dom 和 Redux 的 React 应用程序。我的问题是组件在初始加载时和每次刷新页面时都没有正确加载。无论刷新的组件如何,都会出现此问题。它也只在我实时部署时发生。它在本地没有问题
这里是我的应用程序的简要概述:
我已经尝试了以下解决方案来解决这个问题,但到目前为止都没有奏效:
确保 BrowserRouter 包装整个应用程序。
将 BrowserRouter 和 Routes 组件移动到主 index.js 文件中。
在 Sidebar 组件中实现 useEffect 和 useState 钩子。
Package.json: {
"name": "my-website",
"version": "0.1.0",
"private": true,
"homepage": "https://anderson-cory.com",
"dependencies": {
"-": "^0.0.1",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@material-ui/core": "^4.12.4",
"@mui/icons-material": "^5.11.16",
"@mui/material": "^5.12.2",
"@mui/styles": "^5.12.0",
"@mui/system": "^5.12.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.3.6",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"heapdump": "^0.3.15",
"nodemailer": "^6.9.1",
"package": "^1.0.1",
"pg": "^8.10.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-perfect-scrollbar": "^1.5.8",
"react-redux": "^8.0.5",
"react-router-dom": "^6.10.0",
"react-scripts": "^5.0.1",
"react-scroll": "^1.8.2",
"react-scrollbars-custom": "^4.1.1",
"react-tsparticles": "^2.9.3",
"redux": "^4.2.1",
"tsparticles": "^2.9.3",
"typescript": "^4.4.4",
"web-vitals": "^3.3.1"
},
"scripts": {
"start": "node --max-old-space-size=512 server.js",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"server": "node server.js"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"description": "This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).",
"main": "index.js",
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"nodemon": "^2.0.22",
"uglify-js": "^3.17.4"
}
}
App.js: import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import CssBaseline from '@mui/material/CssBaseline';
import Box from '@mui/material/Box';
import Container from '@mui/material/Container';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { styled } from '@mui/system';
import Sidebar from './components/Sidebar/Sidebar';
import Home from './components/Home/Home';
import About from './components/About/About';
import Projects from './components/Projects/Projects';
import Contact from './components/Contact/Contact';
import Education from './components/Education/Education';
import Experience from './components/Experience/Experience';
import Skills from './components/Skills/Skills';
const theme = createTheme({
palette: {
background: {
default: '#228B22',
},
},
typography: {
fontFamily: 'Roboto, sans-serif',
h1: {
fontSize: '3rem',
fontWeight: 500,
},
h2: {
fontSize: '2.5rem',
fontWeight: 500,
},
h3: {
fontSize: '2rem',
fontWeight: 500,
},
h4: {
fontSize: '1.5rem',
fontWeight: 500,
},
h5: {
fontSize: '1.2rem',
fontWeight: 500,
},
h6: {
fontSize: '1rem',
fontWeight: 500,
},
body1: {
fontSize: '1rem',
},
body2: {
fontSize: '0.875rem',
},
},
});
const AnimatedBackground = styled('div')`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
background: linear-gradient(135deg, #228B22, #FFDB58, #87CEFA, #E6E6FA, #DDA0DD);
background-size: 400% 400%;
animation: gradient 15s ease infinite;
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
`;
const StyledSidebar = styled(Sidebar)(({ theme }) => ({
position: 'fixed',
left: 0,
top: '50%',
transform: 'translateY(-50%)',
}));
const App = () => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setIsMounted(true);
}, 100);
return () => clearTimeout(timer);
}, []);
if (!isMounted) return null;
return (
<ThemeProvider theme={theme}>
<Router>
<CssBaseline />
<AnimatedBackground />
<Box display="flex" minHeight="100vh">
<StyledSidebar />
<Container maxWidth="md" minWidth="60%">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/skills" element={<Skills />} />
<Route path="/projects" element={<Projects />} />
<Route path="/experience" element={<Experience />} />
<Route path="/education" element={<Education />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Container>
</Box>
</Router>
</ThemeProvider>
);
};
export default App;
Sidebar.js:
import React from 'react';
import { NavLink } from 'react-router-dom';
import { Avatar, Box, Typography, IconButton } from '@mui/material';
import { makeStyles } from '@mui/styles';
import {
Home as HomeIcon,
Info as InfoIcon,
Work as WorkIcon,
Code as CodeIcon,
School as SchoolIcon,
Email as EmailIcon,
Build as BuildIcon,
LinkedIn,
GitHub,
} from '@mui/icons-material';
import useTypingEffect from './useTypingEffect';
const useStyles = makeStyles((theme) => ({
avatarContainer: {
display: 'flex',
flexDirection: 'column', // Change flexDirection to 'column'
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(229, 229, 229, 0.8)',
borderRadius: '1rem',
padding: '1rem',
zIndex: 1000,
//marginLeft: '200px',
height: '100%',
width: '100%', // Add a width to the sidebarContainer
marginTop: '11%',
marginBottom: '5%',
boxShadow: '0 0 2rem rgba(0, 0, 0, 0.1)',
border: '3px solid rgba(0, 0, 0, 0.15)',
borderLeft: '10px solid green',
transition: 'all 0.3s ease-in-out',
'&:hover': {
boxShadow: '0 0 4rem rgba(0, 0, 0, 0.2)',
transform: 'scale(1.02)',
},
},
avatar: {
width: '75%',
height: '75%',
objectFit: 'cover',
borderRadius: '50%',
border: '3px solid #000',
},
listItem: {
display: 'flex',
justifyContent: 'flex-start',
flexDirection: 'row',
marginBottom: theme.spacing(1),
backgroundColor: 'rgba(255, 255, 255, 0.8)',
borderRadius: '5px',
padding: '5px',
flexWrap: 'wrap',
maxWidth: '100%', // Add a maxWidth to prevent overflow
overflowX: 'hidden', // Disable scrolling by hiding overflow
overflowY: 'hidden',
},
listItemButton: {
padding: theme.spacing(1),
margin: theme.spacing(0.5),
backgroundColor: 'rgba(34, 139, 34, 0.8)',
borderRadius: '50%',
'&:hover': {
backgroundColor: 'rgba(34, 139, 34, 1)',
transform: 'scale(1.1)',
transition: 'transform 0.3s ease-in-out',
},
flexGrow: 1, // Make the buttons scalable within the container
flexBasis: 'auto', // Allow the buttons to adjust their width
whiteSpace: 'nowrap', // Prevent the button text from wrapping to the next line
width: '20%', // Set the width to be a percentage of the avatar container's width
maxWidth: '100%', // Limit the width of the button to its parent's width
},
'@media (max-width: 600px)': {
listItemButton: {
padding: theme.spacing(0.5),
margin: theme.spacing(0.25),
borderRadius: '50%',
},
},
active: {
color: theme.palette.primary.main,
},
}));
const jobTitles = [
'Web Developer',
'Father',
'Veteran',
'Fantasy Football Champion',
'Full-Stack Developer',
'Cook',
'Software Engineer',
'Gamer',
'Traveler',
'Life Long Learner',
'Investigator',
];
const Sidebar = () => {
const classes = useStyles();
const displayedTitle = useTypingEffect(jobTitles, 150, 2000);
return (
<Box className={classes.sidebarContainer}>
<Box display="flex" flexGrow={1}>{/* Add this wrapper Box */}
{/* Nav links container */}
<Box
display="flex"
flexDirection="column"
alignItems="flex-start" // Align the items to the left side
justifyContent="space-between" // Distribute the items evenly along the column
pl={5}
pr={1}
mt={13}
style={{ height: "100%" }} // Set the height to 100% to fill the available space
>
<NavLink
to="/"
activeClassName={classes.active}
className={classes.listItem}
>
<IconButton className={classes.listItemButton}>
<HomeIcon />
</IconButton>
</NavLink>
<NavLink
to="/about"
activeClassName={classes.active}
className={classes.listItem}
>
<IconButton className={classes.listItemButton}>
<InfoIcon />
</IconButton>
</NavLink>
<NavLink
to="/projects"
activeClassName={classes.active}
className={classes.listItem}
>
<IconButton className={classes.listItemButton}>
<CodeIcon />
</IconButton>
</NavLink>
<NavLink
to="/skills"
activeClassName={classes.active}
className={classes.listItem}
>
<IconButton className={classes.listItemButton}>
<BuildIcon />
</IconButton>
</NavLink>
<NavLink
to="/experience"
activeClassName={classes.active}
className={classes.listItem}
>
<IconButton className={classes.listItemButton}>
<WorkIcon />
</IconButton>
</NavLink>
<NavLink
to="/education"
activeClassName={classes.active}
className={classes.listItem}
>
<IconButton className={classes.listItemButton}>
<SchoolIcon />
</IconButton>
</NavLink>
<NavLink
to="/contact"
activeClassName={classes.active}
className={classes.listItem}
>
<IconButton className={classes.listItemButton}>
<EmailIcon />
</IconButton>
</NavLink>
</Box>
{/* Avatar container */}
<Box className={classes.avatarContainer}>
<Avatar alt="Your Name" src="ANDY.jpeg" className={classes.avatar} />
<Typography variant="h4" mt={2}>
Cory Anderson
</Typography>
<Typography
variant="subtitle1"
mt={1}
sx={{ minHeight: '1.75rem', color: 'rgb(0, 100, 0)' }}
>
<span className="typing-container">{displayedTitle}</span>
</Typography>
<Box mt={2} display="flex" flexDirection="row" gap="16px">
<IconButton
href="https://www.linkedin.com/in/cory-anderson-1b1492117/"
target="_blank"
color="secondary"
>
<LinkedIn />
</IconButton>
<IconButton
href="https://github.com/andydarknessb"
target="_blank"
color="secondary"
>
<GitHub />
</IconButton>
</Box>
</Box>
</Box> {/* Close the wrapper Box */}
</Box>
);
};
export default Sidebar;
Projects.js:
import React from 'react';
import { Box, Typography } from '@material-ui/core';
import { makeStyles, createTheme, ThemeProvider } from '@material-ui/core/styles';
import ScrollableContent from '../ScrollableContent/ScrollableContent';
const theme = createTheme({
typography: {
fontFamily: [
'Roboto',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Oxygen-Sans',
'Ubuntu',
'Cantarell',
'"Helvetica Neue"',
'sans-serif',
].join(','),
},
});
const useStyles = makeStyles((theme) => ({
textBox: {
position: 'relative',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(229, 229, 229, 0.8)',
borderRadius: '1rem',
padding: '1rem 0',
zIndex: 1000,
marginLeft: '0px',
height: '75vh',
marginTop: '11%',
marginBottom: '80%',
marginRight: '80px',
boxShadow: '0 0 2rem rgba(0, 0, 0, 0.1)',
border: '3px solid #333',
transition: 'all 0.3s ease-in-out',
'&:hover': {
boxShadow: '0 0 4rem rgba(0, 0, 0, 0.2)',
transform: 'scale(1.02)',
},
},
title: {
fontWeight: 600,
color: '#333',
marginBottom: theme.spacing(2),
},
content: {
fontSize: '1rem',
lineHeight: 1.6,
color: '#444',
fontWeight: 400,
textAlign: 'justify',
marginBottom: theme.spacing(2),
padding: '5px',
},
}));
function Projects() {
const classes = useStyles();
return (
<ThemeProvider theme={theme}>
<Box className={classes.textBox}>
<Typography variant="h6" gutterBottom className={classes.title}>
Projects:
</Typography>
<ScrollableContent>
<Typography variant="body1" gutterBottom className={classes.content}>
</Typography>
{/* Add CodersRank Portfolio widget */}
<codersrank-portfolio username="andydarknessb"></codersrank-portfolio>
</ScrollableContent>
</Box>
</ThemeProvider>
);
}
export default Projects;
Home.js:
import React, { useState, useEffect } from 'react';
import { Box, Typography } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import ScrollableContent from '../ScrollableContent/ScrollableContent';
const useStyles = makeStyles((theme) => ({
homeContainer: {
position: 'relative',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(229, 229, 229, 0.8)',
borderRadius: '1rem',
padding: '2rem',
zIndex: 1000,
height: '65vh',
marginTop: '11%',
marginBottom: '80%',
marginLeft: 'auto',
marginRight: 'auto',
maxWidth: '80%',
boxShadow: '0 0 2rem rgba(0, 0, 0, 0.1)',
border: '3px solid rgba(0, 0, 0, 0.15)',
borderColor: '#4E4E4E',
transition: 'all 0.3s ease-in-out',
'&:hover': {
boxShadow: '0 0 4rem rgba(0, 0, 0, 0.2)',
transform: 'scale(1.02)',
},
},
title: {
color: '#4E4E4E',
fontWeight: 'bold',
marginBottom: '1rem',
textAlign: 'center',
},
subTitle: {
color: '#4E4E4E',
marginBottom: '2rem',
lineHeight: '1.5',
textAlign: 'center',
},
contentWrapper: {
overflowY: 'auto',
maxHeight: 'calc(65vh - 3rem)',
padding: '0 1rem',
paddingRight: '10px', // Add desired padding value
},
}));
const Home = () => {
const classes = useStyles();
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setIsMounted(true);
}, 100);
return () => clearTimeout(timer);
}, []);
if (!isMounted) return null;
return (
<Box className={classes.homeContainer}>
<Typography variant="h2" component="h1" gutterBottom className={classes.title}>
Welcome to My Website
</Typography>
<ScrollableContent>
<Box pl={5} pr={5}> {/* Add padding to the content */}
<Typography variant="h5" component="p" gutterBottom className={classes.subTitle}>
How a Full Stack Developer Got Lost in a Stack of Pancakes and Still Made It To Your Screen
</Typography>
</Box>
</ScrollableContent>
</Box>
);
};
export default Home;
//Aboutme.js
import React from 'react';
import { Box, Typography,} from '@material-ui/core';
import { makeStyles, createTheme, ThemeProvider } from '@material-ui/core/styles';
import ScrollableContent from '../ScrollableContent/ScrollableContent';
const theme = createTheme({
typography: {
fontFamily: [
'Roboto',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Oxygen-Sans',
'Ubuntu',
'Cantarell',
'"Helvetica Neue"',
'sans-serif',
].join(','),
},
});
const useStyles = makeStyles((theme) => ({
textBox: {
position: 'relative', // Add relative positioning
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(229, 229, 229, 0.8)',
borderRadius: '1rem',
padding: '1rem 0',
zIndex: 1000,
marginLeft: '0px',
height: '75vh',
marginTop: '11%',
marginBottom: '80%',
marginRight: '80px',
boxShadow: '0 0 2rem rgba(0, 0, 255, opacity)',
border: '3px solid #333',
transition: 'all 0.3s ease-in-out',
'&:hover': {
boxShadow: '0 0 4rem rgba(0, 0, 0, 0.2)',
transform: 'scale(1.02)',
},
},
title: {
fontWeight: 600,
color: '#333',
marginBottom: theme.spacing(2),
},
content: {
fontSize: '1rem',
lineHeight: 1.6,
color: '#444',
fontWeight: 400,
textAlign: 'justify',
marginBottom: theme.spacing(2),
padding: '5px', // Add padding to the content
},
}));
function AboutMe() {
const classes = useStyles();
return (
<ThemeProvider theme={theme}>
<Box className={classes.textBox}>
<Typography variant="h6" gutterBottom className={classes.title}>
About Me:
</Typography>
<ScrollableContent>
<Typography variant="body1" gutterBottom className={classes.content}>
</Typography>
<Typography variant="body1" gutterBottom className={classes.content}>
</Typography>
<Typography variant="body1" gutterBottom className={classes.content}>
</Typography>
<Typography variant="body1" gutterBottom className={classes.content}>
</Typography>
</ScrollableContent>
</Box>
</ThemeProvider>
);
}
export default AboutMe;
Contact.js:
import React, { useState } from 'react';
import { Container, Box, TextField, Button, Typography, Snackbar } from '@mui/material';
import { makeStyles } from '@mui/styles';
import MuiAlert from '@mui/material/Alert';
const useStyles = makeStyles((theme) => ({
contactContainer: {
paddingTop: '32px',
marginTop: '64px',
},
contactForm: {
display: 'flex',
flexDirection: 'column',
gap: '16px',
},
contactTextField: {
width: '100%',
},
contactButton: {
marginTop: '16px',
},
}));
const Alert = React.forwardRef(function Alert(props, ref) {
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />;
});
const Contact = () => {
const classes = useStyles();
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
const [open, setOpen] = useState(false);
const handleSubmit = async (event) => {
event.preventDefault();
try {
const response = await fetch('http://localhost:3001/send-message', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, email, message }),
});
if (response.ok) {
setName('');
setEmail('');
setMessage('');
setOpen(true);
} else {
throw new Error('Failed to send message');
}
} catch (error) {
console.error(error);
}
};
const handleClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setOpen(false);
};
return (
<Container maxWidth="sm" className={classes.contactContainer}>
<Typography variant="h4" align="center" gutterBottom>
Contact
</Typography>
<form onSubmit={handleSubmit} className={classes.contactForm}>
<TextField
label="Name"
variant="outlined"
required
className={classes.contactTextField}
value={name}
onChange={(e) => setName(e.target.value)}
/>
<TextField
label="Email"
variant="outlined"
required
className={classes.contactTextField}
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<TextField
label="Message"
variant="outlined"
multiline
rows={4}
required
className={classes.contactTextField}
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<Button type="submit" variant="contained" color="primary" className={classes.contactButton}>
Submit
</Button>
</form>
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
Message sent successfully!
</Alert>
</Snackbar>
</Container>
);
};
export default Contact;
Education.js
import React from 'react';
import { Box, Typography } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
container: {
backgroundColor: 'rgba(229, 229, 229, 0.8)',
padding: theme.spacing(10),
[theme.breakpoints.down('sm')]: {
padding: theme.spacing(5),
},
marginTop: '7rem',
boxShadow: '0 0 2rem rgba(0, 0, 0, 0.1)',
border: '3px solid #333',
borderRadius: '1rem',
},
title: {
fontWeight: 700,
color: '#333',
marginBottom: theme.spacing(2),
textTransform: 'uppercase',
},
education: {
marginTop: theme.spacing(6),
},
educationWidget: {
'--item-bg-color': 'white',
'--item-border-radius': '5px',
'--item-box-shadow': '0 2px 2px rgba(0, 0, 0, 0.05)',
'--title-text-color': '#333',
'--title-font-size': '1.1rem',
'--title-font-weight': 'bold',
'--description-text-color': '#555',
'--description-font-size': '1rem',
'--date-text-color': '#777',
'--date-font-size': '0.9rem',
'--certificate-link-text-color': '#77aaff',
'--certificate-link-text-transform': 'none',
'--certificate-link-hover-text-decoration': 'underline',
'--section-title-text-color': '#777',
'--section-title-font-size': '1.5rem',
'--section-title-font-weight': 'bold',
'--section-title-text-transform': 'uppercase',
},
}));
function Education() {
const classes = useStyles();
return (
<Box className={classes.container}>
<Typography variant="h4" gutterBottom className={classes.title}>
Education
</Typography>
<Box className={classes.education}>
<codersrank-education
username="andydarknessb"
max-items="3"
education="true"
certificates="true"
certificates-link="true"
certificates-link-text="See certification"
education-section-title=""
certificates-section-title=""
branding="false"
className={classes.educationWidget}
></codersrank-education>
</Box>
</Box>
);
}
export default Education;
Experience.js:
import React from 'react';
import { Box, Typography } from '@material-ui/core';
import { makeStyles, createTheme, ThemeProvider } from '@material-ui/core/styles';
import ScrollableContent from '../ScrollableContent/ScrollableContent';
const theme = createTheme({
typography: {
fontFamily: [
'Roboto',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Oxygen-Sans',
'Ubuntu',
'Cantarell',
'"Helvetica Neue"',
'sans-serif',
].join(','),
},
});
const useStyles = makeStyles((theme) => ({
textBox: {
position: 'relative',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(229, 229, 229, 0.8)',
borderRadius: '1rem',
padding: '1rem 0',
zIndex: 1000,
marginLeft: '0px',
height: '75vh',
marginTop: '11%',
marginBottom: '80%',
marginRight: '80px',
boxShadow: '0 0 2rem rgba(0, 0, 0, 0.1)',
border: '3px solid #333',
transition: 'all 0.3s ease-in-out',
'&:hover': {
boxShadow: '0 0 4rem rgba(0, 0, 0, 0.2)',
transform: 'scale(1.02)',
},
},
title: {
fontWeight: 600,
color: '#333',
marginBottom: theme.spacing(2),
},
}));
const Experience = () => {
const classes = useStyles();
return (
<ThemeProvider theme={theme}>
<Box className={classes.textBox}>
<Typography variant="h6" gutterBottom className={classes.title}>
Experience
</Typography>
<ScrollableContent>
<codersrank-work-experience username="andydarknessb" logos></codersrank-work-experience>
</ScrollableContent>
</Box>
</ThemeProvider>
);
};
export default Experience;