首頁  >  問答  >  主體

為什麼 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粉571233520225 天前479

全部回覆(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
  • 取消回覆