【发布时间】:2022-11-12 05:43:38
【问题描述】:
我是 React 18 和 Suspense 的新手。我以前几乎所有的 Web 开发都是在 asp.net mvc 中完成的。我想单击表单上的按钮,将表单输入值传递给具有 [FromQuery] 属性的 Web api HttpGet 方法,然后将返回呈现到 div 中。
如果我在 asp.net mvc 中这样做,我会像在 javascript 中那样连接一个按钮单击事件:
const btnSearch = document.getElementById('btnSearch');
btnSearch.addEventListener("click", function() {
executeMySearch();
return false;
});
在 executeMySearch() 方法中,我会获取表单输入值,将它们发送到服务器,从服务器获取一些 html 并将其放入 div 中,如下所示:
const searchresults = document.getElementById('searchresults');
let formData = new FormData(document.forms[0]);
fetch('/Index?handler=MySearchMethod', {
method: 'post',
body: new URLSearchParams(formData),
}).then(function (response) {
return response.text();
}).then(function (html) {
searchresults.innerHTML = html;
当然,在 React 中方法是完全不同的,我展示上面的代码只是为了演示我想要发生的事情。我希望仅在单击搜索按钮时执行搜索。我的问题是,我无法弄清楚如何管理 React 状态来实现这一点。目前,单击一次搜索按钮后,每次用户更改表单输入的值时,我的搜索都会执行。我理解为什么会发生这种情况,但我不知道如何构建我的组件,以便仅在单击搜索按钮时执行搜索。
在服务器端,我的 web api 接收一个表单并返回一个通用列表,就像这样。这工作正常:
[HttpGet("MySearchMethod")]
public async Task<List<MySearchResult>> MySearchMethod([FromQuery]MySearchForm mySearchForm)
{
return await _myRepository.GetMySearchResults(mySearchForm);
}
在我的 React 应用程序中,我有一个搜索组件。该组件呈现一个包含以下元素的表单:
- 四个选择,其中包含搜索条件。这些选择被包装在 React 组件中。
- 一个搜索按钮
- 呈现搜索结果的组件
每个选择输入都是一个 React 组件,其中包含从 Web api 获取的枚举列表。每个选择都在搜索组件中定义,如下所示:
const MyEnums = lazy(() => import('./MyEnums'));
当定义搜索组件时,这些 React 组件中的每一个都与 React 状态相关联,如下所示:
const MySearchComponent = () => {
const [myEnum, setMyEnum] = useState(0);
function onChangeMyEnum(myEnumId : number){
setMyEnum(myEnumId);
}...
我将我的搜索按钮绑定到 React 状态,如下所示:
const [isSearch, setIsSearch] = useState(false);
我的搜索组件返回一个带有搜索条件和搜索按钮的表单,以及一个包含搜索结果的 div:
return (
<>
<form>
<div>
<ErrorBoundary FallbackComponent={MyErrorHandler}>
<h2>My Search Criteria Select</h2>
<Suspense fallback={<Spinner/>}>
<MyEnums onChange={onChangeMyEnum} />
</Suspense>
</ErrorBoundary>
</div>
<button className='btn btn-blue' onClick={(e) => {
e.preventDefault();
setIsSearch(true);
}
}>Search</button>
</form>
<div>
{
isSearch === true ?
<ErrorBoundary FallbackComponent={MyErrorHandler}>
<Suspense fallback={<Spinner/>}>
<MySearchResults myEnum={myEnum} [..other search criteria] />
</Suspense>
</ErrorBoundary>
: <span> </span>
}
</div>
一切正常。问题是,在第一次单击搜索按钮(执行“setIsSearch(true)”)后,每次用户更改其中一个表单输入中的选择时,搜索都会执行。我明白为什么。我的“isSearch”变量保持为真,因此当表单输入更改更改状态并且重新呈现组件时,搜索再次发生。
我尝试将“setIsSearch”方法传递给 MySearchResults 组件,并在组件呈现后调用 setIsSearch(false),但这当然完全符合它的预期。 React 状态发生变化,组件重新渲染,它看到 "isSearch" 为 false,它使搜索结果消失。当我点击我的搜索按钮时,我看到搜索结果短暂闪烁然后消失,这正是应该发生的事情。
每次选择更改时,我也尝试调用 setIsSearch(false) ,但这当然会导致我的搜索结果消失,这是不希望的。
我错过了什么?我如何构造它以便仅在单击“搜索”按钮时才进行搜索?
附言Web API 调用是在呈现时在 MySearchResults 组件内部进行的。 MySearchResults 组件如下所示:
import React from 'react';
import { useQuery } from 'react-query';
import MySearchResult from './MySearchResult';
const fetchMySearchResults = async (myEnumId : number [...other criteria]) => {
let url = `${process.env.REACT_APP_MY_API}/GetMySearchResults/?myEnumId=${myEnumId}&[...other criterial]`;
const response = await fetch(url);
return response.json();
}
const MySearchResults = (props : any) => {
const {data} = useQuery(['myquery', props.myEnum,...other search criteria...]
,() => fetchMySearchResults(props.myEnun [...other search criteria]),
{
suspense:true
});
return (
<>
<table>
<thead>
<tr>
<th>My Column Header</th>
<th>...and so on</th>
</tr>
</thead>
<tbody>
{data.map((mySearchResult: {
...and so on
</tbody>
</table>
</>
);
};
export default MySearchResults;
【问题讨论】:
-
搜索功能 (executeMySearch()) 实际执行在哪里?
-
@ZaeemKhaliq 我添加了呈现搜索结果的组件。呈现组件时发生搜索。
-
你不需要在
MySearchResults中这样做。只需在搜索按钮的单击事件处理程序上执行此操作。由于您使用的是react-query,因此必须有一些标志或某种方式来手动触发获取。在您的请求完成后,如果返回了一些data,您可以简单地呈现 MySearchResults。 -
@ZaeemKhaliq 当您说“必须有一些标志或某种方式来手动触发获取”时。我不知道该怎么做。就是那个问题。你是怎样做的?