简体   繁体   English

React Router - 单击子组件按钮时如何防止顶级刷新,preventDefault似乎没有什么区别

[英]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在此处输入图像描述

Issues问题

It doesn't seem you understand the React component lifecycle well and are using the completely wrong tool to measure React renders.您似乎没有很好地理解 React 组件的生命周期,并且使用了完全错误的工具来测量 React 渲染。

  1. 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组件中而加剧,该组件会双重安装应用程序/组件以确保可重用状态,并有意双重调用某些方法和用作检测无意副作用的一种方式

  2. 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 函数。

Solution/Suggestion解决方案/建议

  • Move all the 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挂钩中完成。
  • Render 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组件的单个日志。

编辑 react-router-how-to-prevent-top-level-refresh-when-child-component-button-is-c

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 React Router:单击链接/按钮时如何重定向到不同的组件? - React Router: How to redirect to a different component when a link/button is clicked? 在 ReactJS 中按下按钮时,如何使兄弟组件的子组件刷新? - How to make a child component of a sibling component refresh when a button is pressed in ReactJS? 当我将它分离到另一个组件中时,React-router重定向似乎不起作用 - React-router Redirect does not seem to work when I separate it into another component 如何使用react-router将顶级组件状态传递给路由? - How do you pass top level component state down to Routes using react-router? <a>单击标签</a>时如何防止页面刷新<a>?</a> - How to prevent page refresh when <a> tag clicked? (React) 单击提交按钮时如何获取另一个组件的 state? - (React) How to get the state of another component when a submit button is clicked? 如何防止反应路由器多个组件 - how to prevent react router multiple component 如何在父组件上单击按钮时将数据从子组件传递到父组件 - How to pass data from child component to parent component when button clicked on parent component 组件如何知道页面的提交按钮何时被点击? - How does component know that when page's submit button is clicked? 单击添加按钮时显示 React 组件 - Display React Component When the Add Button is Clicked
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM