繁体   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

背景:

我有一个简单的反应路由器:

<BrowserRouter >
    <Routes>
      <Route path="test/:locationId" element={<TestComponent />}></Route>

然后我的组件非常简单:

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>
    );
  }

问题:每当我单击 3 个按钮中的任何一个时,我都会看到TestComponent从顶层刷新,就像TestComponent top level component Mon Jul 18 2022 17:35:52 GMT-0400 (Eastern Daylight Time)

现在我计划在TestComponent中执行一些繁重的 http 请求和 setState() 。 但是,每当我单击按钮(在子组件中)时,此TestComponent总是会刷新,因此会一遍又一遍地执行我想要阻止的 http 请求。 尝试了 e.stopPropagation() 和 e.preventDefault() 但没有发现任何区别。 在此处输入图像描述

问题

您似乎没有很好地理解 React 组件的生命周期,并且使用了完全错误的工具来测量 React 渲染。

  1. 所有控制台日志都在渲染周期之外作为无意的副作用发生。 这意味着任何时候 React 调用您的应用程序(整个应用程序代码)以重新渲染(出于任何原因)组件主体在“渲染阶段”期间执行,以便计算更改和实际需要推送的差异在“提交阶段”期间到 DOM。 提交阶段是 React 将应用程序渲染到 DOM,这就是我们通常俗称的 React 组件渲染。

    在此处输入图像描述

    请注意,“渲染阶段”被认为是一个纯函数,可以在任何 React 需要调用它时调用。 这就是为什么它是纯功能,即没有无意的副作用。 React 函数组件的整个函数体就是“render”方法。

    请注意,“提交阶段”是 UI 已更新到 DOM 并且现在可以运行效果的地方。

    在组件生命周期之外使用console.log的问题通常会因为我们将应用程序渲染到React.StrictMode组件中而加剧,该组件会双重安装应用程序/组件以确保可重用状态,并有意双重调用某些方法和用作检测无意副作用的一种方式

  2. Inner像 React 组件一样定义,但像常规函数一样手动调用。 这是一个相当明显的禁忌。 在 React 中,我们将 React 组件的引用作为 JSX传递给 React。 我们从不直接直接调用我们的 React 函数。

解决方案/建议

  • 将所有console.log语句移动到useEffect钩子中,以便在每个渲染(到 DOM )周期中准确调用它们一次。 所有有意的副作用都应该从useEffect挂钩中完成。
  • Inner渲染为 React 组件并正确地将 props 传递给它。

例子:

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>

在此处输入图像描述

请注意,在初始StrictMode挂载/重新挂载之后,单击按钮只会记录来自每个Inner组件的单个日志。

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

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM