簡體   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