我有一個應用程序,有兩個選項卡“Apple”和“Banana”。每個選項卡都有一個使用 useState
實現(xiàn)的計數(shù)器。
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> ); };
令人困惑的是計數(shù)器狀態(tài)在兩個選項卡之間共享!
如果我增加一個選項卡上的計數(shù)器,然后切換到另一選項卡,計數(shù)器也會發(fā)生變化。
這是為什么?
這是我完整的應用程序:
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 對這里發(fā)生的事情有一個很好的解釋和答案,這是一種優(yōu)化,不會僅僅因為 URL 路徑發(fā)生變化而拆除并重新安裝相同的 React 組件。使用 React 鍵肯定會解決這個問題,強制 React 重新掛載 Tab
組件,從而“重置”count
狀態(tài)。
我建議使用另一種方法,當 name
屬性從 "apple"
更改為 "banana"
時,保持掛載路由組件并簡單地重置 count
狀態(tài),反之亦然。
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 優(yōu)化為您服務,而不是對您不利。
如果您沒有像 name
這樣的傳遞道具可以從中獲取提示,則可以使用 location.pathname
。請注意,這確實將一些內部組件邏輯與外部細節(jié)耦合起來。
示例:
const { pathname } = useLocation(); const [count, setCount] = useState(0); useEffect(() => { setCount(0); }, [pathname, setCount]);
最終,即使您切換路由,您的組件樹也保持相同。
始終是路由器 -> 交換機 -> 路由 -> 選項卡
由于 Switch 的工作方式,React 永遠不會“安裝”新組件,它只是重用舊樹,因為它可以。
我之前遇到過這個問題,解決方法是在某處添加一個鍵,例如在 Tab
或 Route
上。我通常將其添加到 Route
因為它在我看來更有意義:
{ return ; }} /> { return ; }} />
檢查這個堆棧閃電戰(zhàn):
https://stackblitz.com/edit/react-gj5mcv ?file=src/App.js
當然,當每個選項卡卸載時,您的狀態(tài)都會在每個選項卡中重置,這可能是也可能不是理想的。但解決這個問題的方法當然是(如果這對你來說是個問題的話),像往常一樣,提升狀態(tài)。