我有一个应用程序,有两个选项卡“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粉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]);
P粉6086470332024-03-31 18:42:56
最终,即使您切换路由,您的组件树也保持相同。
始终是路由器 -> 交换机 -> 路由 -> 选项卡
由于 Switch 的工作方式,React 永远不会“安装”新组件,它只是重用旧树,因为它可以。
我之前遇到过这个问题,解决方法是在某处添加一个键,例如在 Tab
或 Route
上。我通常将其添加到 Route
因为它在我看来更有意义:
{ return ; }} /> { return ; }} />
检查这个堆栈闪电战:
https://stackblitz.com/edit/react-gj5mcv ?file=src/App.js
当然,当每个选项卡卸载时,您的状态都会在每个选项卡中重置,这可能是也可能不是理想的。但解决这个问题的方法当然是(如果这对你来说是个问题的话),像往常一样,提升状态。