[英]React Router - How to prevent top level refresh when child component button is clicked, preventDefault does not seem to make a difference
Background:背景:
I have a simple react router:我有一个简单的反应路由器:
<BrowserRouter >
<Routes>
<Route path="test/:locationId" element={<TestComponent />}></Route>
Then I have the component really simple:然后我的组件非常简单:
function Inner(props) {
let ele = JSON.stringify(props);
console.log(" this is inner , with " + ele);
const [value, setValue] = useState(parseInt(ele));
return (
<div onClick={(e) => e.stopPropagation()}>
<p> Inner component: {String(ele)}</p>
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
console.log("inner btn clicked for " + value);
setValue(value + 1);
}}
>
{" "}
inner btn with clicked value {value}
</button>
</div>
);
}
export default function TestComponent(props) {
console.log("TestComponent top level component " + new Date());
console.log("I will do some heavy work like a big http request.....")
const [res, setRes] = useState([1, 2, 3]);
let params = useParams();
console.dir(params);
if (res.length > 0) {
console.log("res > 0 ");
return (
<div className="container">
{res.map((ele) => {
return <div key={String(ele)}>{Inner(ele)}</div>;
})}
</div>
);
}
Problem: Anytime I click on any of the 3 buttons, I see TestComponent
refreshes from the top level, like a new line of TestComponent top level component Mon Jul 18 2022 17:35:52 GMT-0400 (Eastern Daylight Time)
.问题:每当我单击 3 个按钮中的任何一个时,我都会看到TestComponent
从顶层刷新,就像TestComponent top level component Mon Jul 18 2022 17:35:52 GMT-0400 (Eastern Daylight Time)
。
Now I plan to do some heavy http request and setState() inside TestComponent
.现在我计划在TestComponent
中执行一些繁重的 http 请求和 setState() 。 However whenever I click on the buttons (in child components) this TestComponent
always refreshes and thus will do the http request over and over again which I want to prevent.但是,每当我单击按钮(在子组件中)时,此TestComponent
总是会刷新,因此会一遍又一遍地执行我想要阻止的 http 请求。 Tried e.stopPropagation() and e.preventDefault() but saw no difference.尝试了 e.stopPropagation() 和 e.preventDefault() 但没有发现任何区别。 enter image description here在此处输入图像描述
It doesn't seem you understand the React component lifecycle well and are using the completely wrong tool to measure React renders.您似乎没有很好地理解 React 组件的生命周期,并且使用了完全错误的工具来测量 React 渲染。
All the console logs are occurring outside the render cycle as unintentional side-effects.所有控制台日志都在渲染周期之外作为无意的副作用发生。 What this means is that anytime React calls your app ( the entire app code ) to rerender ( for any reason ) that the component body is executed during the "render phase" in order to compute a diff for what changed and actually needs to be pushed to the DOM during the "commit phase".这意味着任何时候 React 调用您的应用程序(整个应用程序代码)以重新渲染(出于任何原因)组件主体在“渲染阶段”期间执行,以便计算更改和实际需要推送的差异在“提交阶段”期间到 DOM。 The commit phase is React rendering the app to the DOM and this is what we often refer colloquially as the React component rendering.提交阶段是 React 将应用程序渲染到 DOM,这就是我们通常俗称的 React 组件渲染。
Note that the "render phase" is to be considered a pure function that can be called anytime React needs to call it.请注意,“渲染阶段”被认为是一个纯函数,可以在任何 React 需要调用它时调用。 This is why it's pure function, ie without unintentional side-effects.这就是为什么它是纯功能,即没有无意的副作用。 The entire function body of a React function component is the "render" method. React 函数组件的整个函数体就是“render”方法。
Note the "commit phase" is where the UI has been updated to the DOM and effects can now run.请注意,“提交阶段”是 UI 已更新到 DOM 并且现在可以运行效果的地方。
The issue of using the console.log
outside the component lifecycle is exacerbated usually by the fact that we render our apps into the React.StrictMode
component that does some double mounting of apps/components to ensure reusable state , and intentionally double invokes certain methods and functions as a way to detect unintentional side-effects .在组件生命周期之外使用console.log
的问题通常会因为我们将应用程序渲染到React.StrictMode
组件中而加剧,该组件会双重安装应用程序/组件以确保可重用状态,并有意双重调用某些方法和用作检测无意副作用的一种方式。
Inner
is defined like a React component but called manually like a regular function. Inner
像 React 组件一样定义,但像常规函数一样手动调用。 This is a fairly obvious no-no.这是一个相当明显的禁忌。 In React we pass a reference to our React components to React as JSX .在 React 中,我们将 React 组件的引用作为 JSX传递给 React。 We don't ever directly call our React functions directly.我们从不直接直接调用我们的 React 函数。
console.log
statements into useEffect
hooks so they are called exactly once per render ( to the DOM ) cycle.将所有console.log
语句移动到useEffect
钩子中,以便在每个渲染(到 DOM )周期中准确调用它们一次。 All intentional side-effects should be done from the useEffect
hook.所有有意的副作用都应该从useEffect
挂钩中完成。Inner
as a React component and correctly pass props to it.将Inner
渲染为 React 组件并正确地将 props 传递给它。Example:例子:
function Inner({ ele }) {
useEffect(() => {
console.log("this is inner , with " + ele);
});
const [value, setValue] = useState(ele);
return (
<div onClick={(e) => e.stopPropagation()}>
<p>Inner component: {String(ele)}</p>
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
console.log("inner btn clicked for " + value);
setValue(value + 1);
}}
>
inner btn with clicked value {value}
</button>
</div>
);
}
... ...
function TestComponent(props) {
const [res, setRes] = useState([1, 2, 3]);
let params = useParams();
useEffect(() => {
console.log("TestComponent top level component " + new Date());
console.log("I will do some heavy work like a big http request.....");
console.dir(params);
if (res.length > 0) {
console.log("res > 0 ");
}
});
if (res.length > 0) {
return (
<div className="container">
{res.map((ele) => (
<div key={ele}>
<Inner {...{ ele }} />
</div>
))}
</div>
);
}
}
... ...
<Routes>
<Route path="test/:locationId" element={<TestComponent />} />
</Routes>
Note that after the initial StrictMode
mounting/remount that clicking the buttons only logs a single log from each Inner
component.请注意,在初始StrictMode
挂载/重新挂载之后,单击按钮只会记录来自每个Inner
组件的单个日志。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.