英文:
Filtering data for first time not working with react-query
问题
我有一个基本的ReactJS组件,使用了react-query("@tanstack/react-query":"^4.24.6")
和"react-data-table-component":“^7.5.3”
。数据已加载,我在表格中显示它。标准的东西,运行正常。
我有一个搜索组件,如这里所述,以前是正常工作的。在进行站点UI重新设计时,我将搜索框移到了数据表组件之外。
为了让用户从搜索框中搜索,我正在执行一个看似简单的4步过程:
- 数据加载保持不变:
const { data, isLoading } = useQuery(["whatever"], () => {
return getDataFromBackend();
}, {
refetchInterval: 10000
});
- 我定义了一个新的状态变量,如下所示:
const [filteredData, setFilteredData] = useState(data);
- 我定义了一个onSearch函数来处理搜索框的更改,并根据搜索文本更改filteredData:
const onSearch = (searchText) => {
if (!searchText || searchText.length === 0) {
setFilteredData(data);
return;
}
// 根据搜索文本过滤数据
const filteredData = rawFilesList.filter(item => {
return (
// 根据需要进行筛选的逻辑
)
});
setFilteredData(filteredData);
}
- 最后,我将filteredData传递给表格:
<DataTable
data={filteredData}
/>
这按预期工作,除非react-query的初始值为undefined(第一次加载或者我使用开发工具删除查询值)。在查询加载数据之后,“data”字段会更新,但不会触发重新加载表格。一旦我在搜索字段中输入内容,正常的服务就会恢复,但我不清楚为什么filteredData变量没有正确加载。
我尝试添加显式的setFilteredData,但也没有帮助:
if (isLoading) {
return (
<>Loading..</>
);
}
setFilteredData(data);
我对上述步骤#1进行了以下更改,并且似乎可以工作,但我不确定这是否会破坏isLoading和其他标志的行为:
const { data, isLoading } = useQuery(["whatever"], async () => {
const x = await getDataFromBackend();
setFilteredData(x);
return x;
}, {
refetchInterval: 10000
});
希望能帮助你让第一次加载按预期工作。
英文:
I have a basic ReactJS component that uses react-query ("@tanstack/react-query": "^4.24.6")
and "react-data-table-component": "^7.5.3"
. The data gets loaded, I display it in a table. Standard stuff, works fine.
I had a search component as described here, which was working fine. During site UI redesign, I externalized the search box so it no longer sits within the data table component.
To let users search from the search box, I am doing what seems to be a simple 4-step process:
- Loading data remains the same:
const {data, isLoading} = useQuery(["whatever"], () => {
return getDataFromBackend();
}, {
refetchInterval: 10000
});
- I defined a new state variable, as follows:
const [filteredData, setFilteredData] = useState(data);
- I defined an onSearch function to handle changes to search box, and changed filteredData based on the search text:
const onSearch = (searchText) => {
if (!searchText || searchText.length === 0) {
setFilteredData(data);
return;
}
// Filter data based on search text
const filteredData = rawFilesList.filter(item => {
return (
// Whatever logic needed to filter
)
});
setFilteredData(filteredData);
}
- And finally, I pass filteredData to the table:
<DataTable
data={filteredData}
/>
This works as expected, EXCEPT when the react-query initial value is undefined (on first load or if I remove the query value using the dev tool). After query loads data, the "data" field gets updated, but it does not trigger the table to be reloaded. As soon as I type something into the search field, normal services are restored, but it is not clear to me why the filteredData variable does not get loaded correctly.
I tried adding an explicit setFilteredData, but that also did not help:
if (isLoading) {
return <>
<div>Loading..</div>
</>
}
setFilteredData(data);
I made the following change to step #1 above, and it seems to work, but I am not sure if this breaks the behavior of isLoading and other flags:
const {data, isLoading} = useQuery(["whatever"], async () => {
const x = await getDataFromBackend();
setFilteredData(x);
return x;
}, {
refetchInterval: 10000
});
Any help on getting the first load to work as expected would be appreciated.
答案1
得分: 2
这是因为 useState
的工作方式:
const { data } = useQuery(...)
const [filteredData, setFilteredData] = useState(data);
在首次渲染时,data
将会是 undefined
,所以你将 undefined
传递给了 useState
。当稍后数据到来时,useState
已经被初始化,因此变化的值将被忽略。
你可以选择以下两种方式之一:
- 将事情拆分成多个组件
创建一个调用 useQuery
的组件,渲染加载和错误状态,然后将其作为 initialFilteredData
传递给另一个组件(一旦数据存在)。在那里,你可以将其放入 useState
,因为组件稍后挂载。
更好的方法是:
- 不要将服务器状态存储在
useState
中,只存储用户已更改的内容。
在你的情况下,可以这样简单处理:
const { data } = useQuery(...)
const [userData, setUserData] = useState();
const filteredData = userData ?? data
请注意我们没有传递任何内容给 useState
,因此它将以 undefined
进行初始化,然后我们从那里或服务器值中 派生 filteredData
。这个派生在每次渲染时发生,所以如果来自 useQuery
的 data
发生变化,filteredData
也会跟着变化(除非你已经在本地状态中有了某些内容)。
英文:
That's because of how useState
works:
const { data } = useQuery(...)
const [filteredData, setFilteredData] = useState(data);
data
will be undefined
on the first render, so you pass undefined
to useState
. When data then comes in later, useState
has already been initialized, so the changing value will be ignored.
What you can do is either:
- break the things up into multiple components
Have one component that calls useQuery
, render loading & error states, then pass it as initialFilteredData
to another component (once data exists). There, you can put it into useState
, because the component mounts later.
An even better approach is:
- don't store server state in
useState
, only store what the user has changed.
In your case, this could be as simple as:
const { data } = useQuery(...)
const [userData, setUserData] = useState();
const filteredData = userData ?? data
Note how we pass nothing to useState
, so it will initialize with undefined
, and then, we'll derive filteredData
from that or the server value. This deriving happens on every render, so if data
from useQuery
changes, filteredData
will pick it up (unless you already have something in your local state).
答案2
得分: 0
我知道你已经找到了解决方案,但我只想分享我的小技巧。
我所做的是添加以下代码块:
useEffect(() => {
if (data)
setFilteredData(data)
}, [data])
这样可以确保每当我从 useQuery()
收到新的 data
时,它都会触发我的 setState
函数。
希望这可以在将来类似情况下帮助你。
英文:
I know you already had your solution but I just want to share my trick.
What I did is adding
useEffect(() => {
if (data)
setFilteredData(data)
}, [data])
So it ensure that anytime I receive new data
from the useQuery()
, it always trigger my setState function.
Hope this can help you in any similar situations in the future.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论