首页 > 解决方案 > React js 更新 prop child,不改变组件的状态

问题描述

在此处输入图像描述

正如您从图像中看到的那样,我有一个页面,在父页面上可以找到抽屉的部分和暗模式更改。

单词Page1input 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>
    </>
  );
}

标签: javascriptreactjscomponentsreact-propsreact-component

解决方案


推荐阅读