我是React 18和Suspense的新手。我以前几乎所有的Web开发都是在www.example.com mvc中完成asp.net的。我想单击表单上的一个按钮,将表单输入值传递给具有[FromQuery]属性的Web API HttpGet方法,并将返回值呈现到div中。
如果我在www.example.com mvc中这样做asp.net,我会在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应用程序中,我有一个搜索组件,该组件呈现一个包含以下元素的表单:
- 四个select,其中包含搜索标准。这些select包含在React组件中。
- 搜索按钮
- 呈现搜索结果的组件
每个select输入都是一个React组件,其中包含从Web API获取的枚举列表。每个select都在搜索组件中定义,如下所示:
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);
My search组件返回一个包含搜索条件和搜索按钮的表单,以及一个包含搜索结果的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”变量保持为true,因此当表单输入更改导致状态改变时,重新呈现组件时,搜索会再次发生。
我尝试将“setIsSearch”方法传递到MySearchResults组件中,并调用setIsSearch(false),但这当然是它应该做的。React状态改变,组件重新呈现,它看到“isSearch”为false,并使搜索结果消失。当我单击搜索按钮时,我看到搜索结果短暂闪烁,然后消失,这正是应该发生的。
我还尝试在每次select更改时调用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;
3条答案
按热度按时间fiei3ece1#
将
useQuery
挂钩从MySearchResults
上移到MySearchComponent
。在useQuery
函数中使用enabled
标志,该标志用于阻止查询自动运行:-当
isSearch
设置为true时(点击Search按钮时),查询将执行。当你得到data
时,渲染MySearchResults
组件,提供data
作为属性。之后,如果您想再次触发搜索功能,可以在搜索按钮的事件处理程序中使用
useQuery
挂钩提供的refetch
函数,如下所示:-如需完整指涉,请造访:-
https://react-query-v3.tanstack.com/reference/useQuery
wmtdaxz32#
使用2 useState:
如果你只使用myEnum,每当有人输入一个新值时,它就会触发一个渲染,从而触发你的搜索。如果你通过设置当你点击按钮时触发搜索的值来分离输入和搜索,你的用户可以输入输入,但只有点击才会触发搜索。因为只有点击才会改变搜索所监听的值。
xdyibdwo3#
大多数答案是这样的--如果不希望组件重新呈现,就不要用useState()设置变量,而是使用useRef()。
因此,现在每个select组件的更改处理程序的定义如下:
这是之前我(错误地)调用useState时的样子:
然后,我将这些变量传递给组件,方法与之前相同:
可防止select中的每一个变更都导致元件重新呈现的问题。
按钮单击调用setIsSearch(true),但此状态需要设置为“false”,以便组件在后续按钮单击时重新呈现。为此,请添加对useEffect的调用:
最后一步是将isSearch变量传递到MySearchResults组件中。根据上面的@ZaeemKhaliq答案,我将其添加到了useQuery调用中,这样,除非用户单击了搜索按钮,否则我就不必运行查询了。这是必需的,因为useQuery不能有条件地运行;也就是说,在TemplateSearchResults组件中,我不能简单地在useQuery语句上方放置一个if块,然后在props.isSearch为false时返回。
下面是MySearchResults组件现在的样子:
在我的父搜索页面上,我删除了搜索结果组件呈现周围的“if”块,而是只呈现,传递“isSearch”变量,以便组件可以决定呈现什么:
哇。所有这些都让我想起了对核能的旧批评:这是一个非常复杂的生成html的方法。