javascript - React js 更新 prop child,不改变组件的状态
问题描述
正如您从图像中看到的那样,我有一个页面,在父页面上可以找到抽屉的部分和暗模式更改。
单词Page1
和input field
出现的位置,在子页面中。
当主题更改,然后切换到暗模式时,带有darkState
状态的道具会从父级传递给子级。
正如您从图像中看到的那样,如果我有一个正在写入的输入字段,那么使用一些文本,然后我切换到dark mode
或打开drawer
.
该组件更新所有内容,丢失其所有内部状态。
我考虑过使用useMemo
,但我不知道我应该在哪里使用它。
你能帮我个忙吗?
链接:https ://codesandbox.io/s/competent-sara-dru7w?file=/src/page/Page1.js
应用程序.js
import React from "react";
import PropTypes from "prop-types";
import { Switch, Route, Link, useLocation } from "react-router-dom";
import {
AppBar,
CssBaseline,
Drawer,
Hidden,
IconButton,
List,
ListItem,
ListItemIcon,
ListItemText,
Toolbar,
Chip
} from "@material-ui/core";
import { GTranslate, Menu } from "@material-ui/icons";
import {
makeStyles,
useTheme,
createMuiTheme,
ThemeProvider
} from "@material-ui/core/styles";
import { blue, grey } from "@material-ui/core/colors";
import DarkModeToggle from "react-dark-mode-toggle";
import { Page1, Page2, Error } from "./page";
import "./styles/main.css";
import "./App.css";
const drawerWidth = 240;
function App(props) {
const { wind } = props;
const container = wind !== undefined ? () => wind().document.body : undefined;
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const localDark = localStorage.getItem("dark");
const isDark = localDark === null ? prefersDark : localDark === "true";
let location = useLocation();
let pathname = location.pathname.replace("/", "");
if (pathname === "") pathname = "page1";
const [state, setState] = React.useState({
mobileOpen: false,
darkState: isDark,
repo: []
});
const { mobileOpen, darkState } = state;
const useStyles = makeStyles((theme) => ({
root: {
display: "flex"
},
drawer: {
[theme.breakpoints.up("sm")]: {
width: drawerWidth,
flexShrink: 0
}
},
appBar: {
[theme.breakpoints.up("sm")]: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth
}
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up("sm")]: {
display: "none"
},
backgroundColor: darkState ? grey[900] : blue[500]
},
// necessary for content to be below app bar
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
color: "#ffffff",
backgroundColor: darkState ? grey[900] : blue[500]
},
content: {
flexGrow: 1,
padding: theme.spacing(3)
}
}));
const palletType = darkState ? "dark" : "light";
const mainPrimaryColor = darkState ? grey[900] : blue[500];
const mainSecondaryColor = darkState ? grey[800] : blue[300];
const darkTheme = createMuiTheme({
palette: {
type: palletType,
primary: {
main: mainPrimaryColor
},
secondary: {
main: mainSecondaryColor
}
}
});
const classes = useStyles();
const theme = useTheme();
const handleDrawerToggle = () =>
setState((prev) => ({ ...prev, mobileOpen: !mobileOpen }));
const changePage = (page) => setState((prev) => ({ ...prev, page }));
const handleThemeChange = React.useCallback(() => {
localStorage.setItem("dark", !darkState);
setState((prev) => ({ ...prev, darkState: !prev.darkState }));
}, []);
const menu = [
{ title: "Page1", path: "page1", icon: <GTranslate /> },
{ title: "Page2", path: "page2", icon: <GTranslate /> }
];
const routeObj = [
{ path: "/", obj: <Page1 darkState={darkState} /> },
{ path: "page1", obj: <Page1 darkState={darkState} /> },
{ path: "page2", obj: <Page2 darkState={darkState} /> }
];
const drawer = (
<div className="mt-32">
<div className={classes.toolbar} />
<List>
{menu.map(({ title, path, icon, badge }, index) => (
<Link to={`/${path}`} key={title}>
<ListItem button key={title} onClick={() => changePage(path)}>
<ListItemIcon
style={{ color: path === pathname ? "#ffffff" : "#ffffff80" }}
>
{icon}
</ListItemIcon>
<ListItemText
primary={<span className="font-bold">{title}</span>}
style={{ color: path === pathname ? "#ffffff" : "#ffffff80" }}
/>
{badge && (
<Chip
label={badge}
size="small"
color="secondary"
className="font-bold"
style={{ color: "#ffffff" }}
/>
)}
</ListItem>
</Link>
))}
</List>
</div>
);
return (
<ThemeProvider theme={darkTheme}>
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={classes.appBar}
style={{
backgroundColor: darkState ? "#303030" : grey[50],
boxShadow: "none"
}}
>
<Toolbar className={"shadow-none"}>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<Menu />
</IconButton>
<div className="ml-auto text-right flex">
<DarkModeToggle
onChange={handleThemeChange}
checked={darkState}
size={60}
/>
</div>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer
container={container}
variant="temporary"
anchor={theme.direction === "rtl" ? "right" : "left"}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper
}}
ModalProps={{
keepMounted: true // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer
classes={{
paper: classes.drawerPaper
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
<div className={classes.toolbar} />
<Switch>
{routeObj.map(({ path, obj }, key) => (
<Route exact path={`/${path}`} component={() => obj} key={key} />
))}
<Route component={() => <Error darkState={darkState} />} />
</Switch>
</main>
</div>
</ThemeProvider>
);
}
App.propTypes = {
/**
* Injected by the documentation to work in an iframe.
* You won't need it on your project.
*/
wind: PropTypes.func
};
export default App;
Page1.js
import React, { useState, useEffect } from "react";
import { TextField, makeStyles } from "@material-ui/core";
import { className } from "../function";
import "../styles/main.css";
export default function Page1({ darkState }) {
const useStyles = makeStyles((theme) => ({
title: {
color: darkState ? "#ffffff" : "#343a40",
textShadow: `3px 3px 2px ${
darkState ? "rgba(0, 0, 0, 1)" : "rgba(150, 150, 150, 1)"
}`
},
button: {
margin: theme.spacing(1)
}
}));
const classes = useStyles();
const [state, setState] = useState({
name: ""
});
const { name } = state;
useEffect(() => {
console.log(darkState, state);
}, []);
useEffect(() => {
console.log("darkState", darkState, state);
}, [darkState]);
const onChange = ({ target: { value } }, name) => {
setState((prev) => ({ ...prev, [name]: value }));
};
console.log(state);
return (
<>
<h1 className={className(classes.title, "text-6xl font-bold hp")}>
Page
<span className="text-primary">1</span>
</h1>
<div
style={{
width: "50%",
minHeight: "600px"
}}
>
<div style={{ paddingBottom: 15 }}>
<TextField
fullWidth
id="outlined-basic"
label={"Name"}
variant="outlined"
size="small"
value={name}
onChange={(value) => onChange(value, "name")}
/>
</div>
</div>
</>
);
}
解决方案
推荐阅读
- javascript - 通过 ajax 调用在 XML 文件中输出注释
- javascript - 如何在 SVG 上使用 preserveAspectRatio
使用 SVG.JS - spring-boot - Gradle 构建出现 java.lang.NoClassDefFoundError
- kubernetes - Istio 允许所有出站流量
- android - 如何在 Android 中私下播放视频
- java - 为什么我的网格窗格中的节点位置会影响我的网格布局?
- android - Android - EditText 边框不可见
- python - 导入 imblearn 时的问题
- ios - 如何在 UITextView textViewDidBeginEditing 中手动显示/停止键盘
- sql - Oracle 插入性能