搜索

首页  >  问答  >  正文

为什么 useState 会在具有不同 props 的路由之间共享?

我有一个应用程序,有两个选项卡“Apple”和“Banana”。每个选项卡都有一个使用 useState 实现的计数器。

const Tab = ({ name, children = [] }) => {
  const id = uuid();
  const [ count, setCount ] = useState(0);

  const onClick = e => {
    e.preventDefault();
    setCount(c => c + 1);
  };

  const style = {
    background: "cyan",
    margin: "1em",
  };

  return (
    <section style={style}>
      <h2>{name} Tab</h2>
      <p>Render ID: {id}</p>
      <p>Counter: {count}</p>
      <button onClick={onClick}>+1</button>
      {children}
    </section>
  );
};

令人困惑的是计数器状态在两个选项卡之间共享!

如果我增加一个选项卡上的计数器,然后切换到另一选项卡,计数器也会发生变化。

这是为什么?


这是我完整的应用程序:

import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { v4 as uuid } from "uuid";
import { HashRouter as Router, Switch, Route, Link } from "react-router-dom";

const Tab = ({ name, children = [] }) => {
  const id = uuid();
  const [ count, setCount ] = useState(0);

  const onClick = e => {
    e.preventDefault();
    setCount(c => c + 1);
  };

  const style = {
    background: "cyan",
    margin: "1em",
  };

  return (
    <section style={style}>
      <h2>{name} Tab</h2>
      <p>Render ID: {id}</p>
      <p>Counter: {count}</p>
      <button onClick={onClick}>+1</button>
      {children}
    </section>
  );
};

const App = () => {
  const id = uuid();

  return (
    <Router>
      <h1>Hello world</h1>
      <p>Render ID: {id}</p>
      <ul>
        <li>
          <Link to="/apple">Apple</Link>
        </li>
        <li>
          <Link to="/banana">Banana</Link>
        </li>
      </ul>
      <Switch>
        <Route
          path="/apple"
          exact={true}
          render={() => {
            return <Tab name="Apple" />;
          }}
        />
        <Route
          path="/banana"
          exact={true}
          render={() => {
            return <Tab name="Banana" />;
          }}
        />
      </Switch>
    </Router>
  );
};

const container = document.getElementById("root");
const root = createRoot(container);

root.render(<App />);

版本:

  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router": "5.2.1",
    "react-router-dom": "5.2.1",
    "uuid": "^9.0.0"
  },

P粉571233520P粉571233520227 天前482

全部回复(2)我来回复

  • P粉496886646

    P粉4968866462024-03-31 18:52:50

    Adam 对这里发生的事情有一个很好的解释和答案,这是一种优化,不会仅仅因为 URL 路径发生变化而拆除并重新安装相同的 React 组件。使用 React 键肯定会解决这个问题,强制 React 重新挂载 Tab 组件,从而“重置”count 状态。

    我建议使用另一种方法,当 name 属性从 "apple" 更改为 "banana" 时,保持挂载路由组件并简单地重置 count 状态,反之亦然。

    const Tab = ({ name, children = [] }) => {
      const id = uuid();
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        setCount(0); // <-- reset to 0 when name prop updates
      }, [name, setCount]);
    
      const onClick = e => {
        e.preventDefault();
        setCount(c => c + 1);
      };
    
      const style = {
        background: "cyan",
        margin: "1em",
      };
    
      return (
        

    {name} Tab

    Render ID: {id}

    Counter: {count}

    {children}
    ); };

    这将使 RRD 优化为您服务,而不是对您不利。

    如果您没有像 name 这样的传递道具可以从中获取提示,则可以使用 location.pathname 。请注意,这确实将一些内部组件逻辑与外部细节耦合起来。

    示例:

    const { pathname } = useLocation();
    const [count, setCount] = useState(0);
    
    useEffect(() => {
      setCount(0);
    }, [pathname, setCount]);
    

    回复
    0
  • P粉608647033

    P粉6086470332024-03-31 18:42:56

    这与Switch 在react-router-dom中工作

    最终,即使您切换路由,您的组件树也保持相同。

    始终是路由器 -> 交换机 -> 路由 -> 选项卡

    由于 Switch 的工作方式,React 永远不会“安装”新组件,它只是重用旧树,因为它可以。

    我之前遇到过这个问题,解决方法是在某处添加一个键,例如在 TabRoute 上。我通常将其添加到 Route 因为它在我看来更有意义:

    
             {
                return ;
              }}
            />
             {
                return ;
              }}
            />
          

    检查这个堆栈闪电战:

    https://stackblitz.com/edit/react-gj5mcv ?file=src/App.js

    当然,当每个选项卡卸载时,您的状态都会在每个选项卡中重置,这可能是也可能不是理想的。但解决这个问题的方法当然是(如果这对你来说是个问题的话),像往常一样,提升状态。

    回复
    0
  • 取消回复