如何处理 React History.Push() 导致不需要的组件重新安装?

Ray*_*Ray 5 javascript typescript reactjs react-router

问题

history.push()在组件内部调用似乎会导致整个 react 组件卸载和重新安装;导致无意义的远程服务调用。

具体来说,我有一个在组件进入时触发的远程服务调用。我不希望重新挂载组件,也不希望重新运行服务调用(速度很慢)。

似乎无论如何history.push(location.pathname + '?' + encodeURI(urlSearchParams.toString()));都会导致卸载。我使用它不正确吗?是否有更好的方法来跟踪用户过滤器更改的历史记录而不必担心无关的服务调用?

意图

我正在利用history.push()查询参数的更改来更新浏览器历史记录。查询参数控制表数据的过滤,例如?sort=asc&isCompleted=true等。

当用户更改他们的过滤设置时,我打算简单地过滤存储在 state 中的现有表数据,而不是远程重新加载数据并迫使用户坐等。我还希望用户能够与包含适当过滤器的其他用户共享 URL。

我试过的

  • 尝试完全删除 history.push(),仅使用状态。这有效,但意味着它不可能有一个可共享的 URL,其中过滤器附加为查询参数。
  • 尝试修改 useEffect() 和 useRef() 但对不断重新安装感到沮丧。

组件代码

import React, { useEffect, useState } from 'react';
import { useLocation, useHistory } from 'react-router-dom';

function useQuery() {
  return new URLSearchParams(useLocation().search);
}

export const WidgetTable = () => {
  let urlSearchParams = useQuery();
  let history = useHistory();
  let location = useLocation();

  const [originalTableData, setOriginalTableData] = useState<TableData| undefined>(undefined);
  const [filteredTableData, setFilteredTableData] = useState<TableData| undefined>(undefined);

  // go get the table data from the remote service
  const fetchTableData = async () => {
   <- go remotely fetch table data and then set originalTableData ->
  }

  // triggered when a user sets a filter on the table (updates the data displayed in the table)
  const filterTableData = () => {
   <- filter the existing table data in state and then set the filterdTableData ->
  }

  // also triggered when a user sets a filter on the table (updates the URL in the browser)
  const setFilter = (filterToSet: ReleasePlanFilterType, value: string) => {
    switch (filterToSet) {
      case ReleasePlanFilterType.Target: {
        if (urlSearchParams.get(filterToSet)) {
          urlSearchParams.set(filterToSet, value);
        } else {
          urlSearchParams.append(filterToSet, value);
        }
        break;
      }
      <snip>
    }

   // We've set the filter in the query params, but persisting this to the history causes a reload :(
   history.push(location.pathname + '?' + encodeURI(urlSearchParams.toString())); 
  
  }

  useEffect(() => {
    fetchTableData();
  }, []);

  return (<snip> a fancy table and filtering controls <snip>);

}

Run Code Online (Sandbox Code Playgroud)

Woj*_*tek 6

经过几个小时的调试后,我设法使用以下方法解决了这个问题:

<Route exact path="/">
  <Home />
</Route>
Run Code Online (Sandbox Code Playgroud)

代替:

<Route exact path="/" component={Home} />
Run Code Online (Sandbox Code Playgroud)

这适用于react-routerv5。

  • 我确认这也是我所看到的。知道为什么会发生这种情况吗? (2认同)
  • 我相信这是库中的一个错误。 (2认同)

Chr*_*ort 5

据我所知,在阅读了其他几个类似的堆栈溢出问题后,似乎没有办法不重新安装(重新渲染)组件。历史更改会自动导致响应路由器 dom 如何处理引擎盖下的事情的道具更改。我发现这些问题中的大多数都使用了类组件,答案是使用 shouldComponentUpdate 来查看以前的路径是否等于新路径。如果它们相等,那么它们将返回,基本上使它们不会重新渲染。

shouldComponentUpdate: function(nextProps, nextState) {
  if(this.props.route.path == nextProps.route.path) return false;
  return true;
}
Run Code Online (Sandbox Code Playgroud)

来源:防止 react-router history.push 重新加载当前路由

所以现在的问题是将其转换为使用钩子。我需要运行一些测试,但我相信这应该适用于您的用例。

useEffect(() => {
    fetchTableData();
}, [location.pathname]);
Run Code Online (Sandbox Code Playgroud)

通过添加location.pathname为依赖项,只有在路径名实际更改时才应调用此效果。