javascript - 使用 useState() 共享组件状态以实现导航中的事件链接

标签 javascript reactjs react-hooks material-ui

我正在尝试学习 React,并且对该框架相当陌生。我正在尝试创建一个简单的导航栏组件,其中包含响应式的material-ui(将显示中型设备及以上设备上的所有链接,并在小型设备上打开侧抽屉)。我已根据自己的喜好进行了大部分设置,但是,我当前遇到的问题是根据我所在的页面获取和设置事件链接。

它似乎可以在中型设备及以上设备上正常工作,但是当转换到较小的设备时,链接无法正确更新,因为它将保留中型屏幕集中的事件链接,同时更新侧抽屉事件链接.

Navbar.js

const Navbar = () => {
    const classes = useStyles();
    const pathname = window.location.pathname;
    const path = pathname === '' ? '' : pathname.substr(1);
    const [selectedItem, setSelectedItem] = useState(path);

    const handleItemClick = (event, selected) => {
        setSelectedItem(selected);
        console.log(selectedItem);
    };

    return (
        <>
            <HideNavOnScroll>
                <AppBar position="fixed">
                    <Toolbar component="nav" className={classes.navbar}>
                        <Container maxWidth="lg" className={classes.navbarDisplayFlex}>
                            <List>
                                <ListItem
                                    button
                                    component={RouterLink}
                                    to="/"
                                    selected={selectedItem === ''}
                                    onClick={event => handleItemClick(event, '')}
                                >
                                    <ListItemText className={classes.item} primary="Home" />
                                </ListItem>
                            </List>

                            <Hidden smDown>
                                <List
                                    component="nav"
                                    aria-labelledby="main navigation"
                                    className={classes.navListDisplayFlex}
                                >
                                    <ListItem
                                        button
                                        component={RouterLink}
                                        to="/account/login"
                                        selected={selectedItem === 'account/login'}
                                        onClick={event => handleItemClick(event, 'account/login')}
                                    >
                                        <ListItemText className={classes.item} primary="Login" />
                                    </ListItem>

                                    <ListItem
                                        button
                                        component={RouterLink}
                                        to="/account/register"
                                        selected={selectedItem === 'account/register'}
                                        onClick={event => handleItemClick(event, 'account/register')}
                                    >
                                        <ListItemText className={classes.item} primary="Register" />
                                    </ListItem>

                                </List>
                            </Hidden>

                            <Hidden mdUp>
                                <SideDrawer />
                            </Hidden>
                        </Container>
                    </Toolbar>
                </AppBar>
            </HideNavOnScroll>

            <Toolbar id="scroll-to-top-anchor" />

            <ScrollToTop>
                <Fab aria-label="Scroll back to top">
                    <NavigationIcon />
                </Fab>
            </ScrollToTop>
        </>
    )
}

SideDrawer.js

const SideDrawer = () => {
  const classes = useStyles();
  const [state, setState] = useState({ right: false });

  const pathname = window.location.pathname;
  const path = pathname === "" ? "" : pathname.substr(1);
  const [selectedItem, setSelectedItem] = useState(path);

  const handleItemClick = (event, selected) => {
    setSelectedItem(selected);
    console.log(selectedItem);
  };

  const toggleDrawer = (anchor, open) => (event) => {
    if (
      event &&
      event.type === "keydown" &&
      (event.key === "Tab" || event.key === "Shift")
    ) {
      return;
    }

    setState({ ...state, [anchor]: open });
  };

  const drawerList = (anchor) => (
    <div
      className={classes.list}
      role="presentation"
      onClick={toggleDrawer(anchor, false)}
      onKeyDown={toggleDrawer(anchor, false)}
    >
      <List component="nav">
        <ListItem
          button
          component={RouterLink}
          to="/account/login"
          selected={selectedItem === "account/login"}
          onClick={(event) => handleItemClick(event, "account/login")}
        >
          <ListItemText className={classes.item} primary="Login" />
        </ListItem>

        <ListItem
          button
          component={RouterLink}
          to="/account/login"
          selected={selectedItem === "account/register"}
          onClick={(event) => handleItemClick(event, "account/register")}
        >
          <ListItemText className={classes.item} primary="Register" />
        </ListItem>
      </List>
    </div>
  );

  return (
    <React.Fragment>
      <IconButton
        edge="start"
        aria-label="Menu"
        onClick={toggleDrawer("right", true)}
      >
        <Menu fontSize="large" style={{ color: "white" }} />
      </IconButton>

      <Drawer
        anchor="right"
        open={state.right}
        onClose={toggleDrawer("right", false)}
      >
        {drawerList("right")}
      </Drawer>
    </React.Fragment>
  );
};

代码沙箱 - https://codesandbox.io/s/async-water-yx90j

我在SO上遇到了这个问题:Is it possible to share states between components using the useState() hook in React? ,这表明我需要将状态提升到共同的祖先组件,但我不太明白如何在我的情况下应用它。

最佳答案

我建议暂时搁置您的代码并为这种提升状态理解做一个 Playground 。提升状态是在不相关的组件之间共享状态的基本策略。基本上,状态和 setState 将存在于某个共同的祖先中。在那里你可以作为 props 传递给它的 child :

const Parent = () => {

  const [name, setName] = useState('joe')

  return (
    <>
      <h1>Parent Component</h1>
      <p>Child Name is {name}</p>
      <FirstChild name={name} setName={setName} />
      <SecondChild name={name} setName={setName} />
    </>
  )
}

const FirstChild = ({name, setName}) => {
  return (
    <>
      <h2>First Child Component</h2>
      <p>Are you sure child is {name}?</p>
      <button onClick={() => setName('Mary')}>My Name is Mary</button>
    </>
  )
}

const SecondChild = ({name, setName}) => {
  return (
    <>
      <h2>Second Child Component</h2>
      <p>Are you sure child is {name}?</p>
      <button onClick={() => setName('Joe')}>My Name is Joe</button>
    </>
  )
}

如您所见,只有一种状态,一种真理来源。状态位于Parent,并向下传递给其children。现在,有时如果您需要您的状态位于某个遥远的曾祖 parent 处,这可能会很麻烦。你必须传递每个 child 直到到达那里,这很烦人。如果您发现自己处于这种情况,您可以使用 React Context API 。而且,对于最复杂的状态管理,有类似 redux 的解决方案。 .

关于javascript - 使用 useState() 共享组件状态以实现导航中的事件链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65052188/

相关文章:

javascript - AngularJS:全局访问服务?

reactjs - useEffect props 回调函数导致无限循环

reactjs - 如何解决React中的 "Invalid hook call"错误

reactjs - useRef 来存储之前的状态值

html - 为什么 Grommet 库中的 Text 组件渲染的 span 元素能够获得 margin-top?

javascript - react 如何将数据从后端推送到前端

javascript - CKEditor 如何以允许 React 识别的方式与 React.js 一起使用?

javascript - 在更改时删除对选择元素的关注,如果没有使用 jquery 更改

javascript - Js,将静态函数从Class设置为变量对象时 'this'是什么

javascript - 插入 HTML 的 React 代码似乎不起作用