javascript - 如何处理导致不需要的组件重新安装的反应 History.Push()?
问题描述
问题
在组件内部调用history.push()
似乎会导致整个反应组件卸载和重新安装;导致毫无意义的远程服务调用。
具体来说,我有一个在组件条目上触发的远程服务调用。我不希望组件重新安装,也不希望服务调用重新运行(它很慢)。
似乎无论如何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>);
}
解决方案
From what I can tell, after reading through several other similar stack overflow questions, there doesn't seem to be a way to not remount (rerender) the component. The history changes automatically causes a prop change from how react router dom handles things under the hood. Most of these questions that I find used class components and the answer was to use shouldComponentUpdate to see if the previous path was equal to the new path. If they were equal then they would return, essentially making it so they wouldn't rerender.
shouldComponentUpdate: function(nextProps, nextState) {
if(this.props.route.path == nextProps.route.path) return false;
return true;
}
Source: Prevent react-router history.push from reloading current route
So now the issue would be converting this to work with hooks. I'll need to run some tests, but I believe this should work for your use case.
useEffect(() => {
fetchTableData();
}, [location.pathname]);
通过添加location.pathname
为依赖项,仅当路径名实际更改时才应调用此效果。
推荐阅读
- matplotlib - Matplotlib:用箱线图替换散点图轴
- api - (ssg-wsg) 有没有办法从 api 获得完整的课程参考号?
- multithreading - 如何正确计算并行总和?
- node.js - 在猫鼬中获得共同的追随者
- python - 如果您两次导入一个模块,一次在顶级脚本中,一次在顶级脚本导入的模块之一中,会发生什么情况?
- wkhtmltopdf - 如何修复 wkhtmltopdf 中的 SVG 颜色渐变渲染
- swift - Swift - 在表格视图中列出来自 Firebase 的数据
- symfony - 从控制台命令运行时,mpdf 无法打开图像
- karate - 有没有更简单的方法可以从空手道测试中调用 Java 断言并为断言提供失败消息?
- excel - Excel公式到三个标准的条件排名?