我有一個應用程序,有兩個選項卡“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"
},
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); // { 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]);這與
Switch在react-router-dom中工作#最終,即使您切換路由,您的元件樹也保持相同。
總是路由器 -> 交換器 -> 路由 -> 選項卡
由於 Switch 的工作方式,React 永遠不會「安裝」新元件,它只是重複使用舊樹,因為它可以。
我之前遇到過這個問題,解決方法是在某處添加一個鍵,例如在
Tab或Route上。我通常將其添加到Route因為它在我看來更有意義:檢查這個堆疊閃電戰:
https://stackblitz.com/edit/react-gj5mcv ?file=src/App.js
#當然,當每個選項卡卸載時,您的狀態都會在每個選項卡中重置,這可能是也可能不是理想的。但解決這個問題的方法當然是(如果這對你來說是個問題的話),像往常一樣,提升狀態。