我有一个应用程序,有两个选项卡“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
当然,当每个选项卡卸载时,您的状态都会在每个选项卡中重置,这可能是也可能不是理想的。但解决这个问题的方法当然是(如果这对你来说是个问题的话),像往常一样,提升状态。