reactjs 使用React 18和Suspension,从表单进行搜索,并在单击按钮时使用搜索结果填充div

64jmpszr  于 2022-11-29  发布在  React
关注(0)|答案(3)|浏览(136)

我是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>&nbsp;</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;
fiei3ece

fiei3ece1#

useQuery挂钩从MySearchResults上移到MySearchComponent。在useQuery函数中使用enabled标志,该标志用于阻止查询自动运行:-

const {data} = useQuery(['myquery', props.myEnum,...other search criteria...]
    ,() => fetchMySearchResults(props.myEnun [...other search criteria]),
    {
        enabled: isSearch,
        suspense:true
    }
);

isSearch设置为true时(点击Search按钮时),查询将执行。当你得到data时,渲染MySearchResults组件,提供data作为属性。
之后,如果您想再次触发搜索功能,可以在搜索按钮的事件处理程序中使用useQuery挂钩提供的refetch函数,如下所示:-

const {data, refetch} = useQuery();
const onSearchClick = (e) => {
  e.preventDefault();
  if(isSearch === true){
    refetch();  
  }else{
    setIsSearch(true);
  }
}

如需完整指涉,请造访:-
https://react-query-v3.tanstack.com/reference/useQuery

wmtdaxz3

wmtdaxz32#

使用2 useState:

const [myEnum, setMyEnum] = useState(0);
const [searchEnum, setSearchEnum] = useState();
setIsSearch(true);

    function onChangeMyEnum(myEnumId : number){
        setMyEnum(myEnumId);
    }

<Suspense fallback={<Spinner/>}>
     <MyEnums onChange={onChangeMyEnum} />
</Suspense>

<button className='btn btn-blue' onClick={(e) => {
                e.preventDefault();
                setSearchEnum(myEnum)
                setIsSearch(true);

            }
                }>Search</button>

{
            isSearch === true ?
                <ErrorBoundary FallbackComponent={MyErrorHandler}>
                    <Suspense fallback={<Spinner/>}>
                        <MySearchResults myEnum={searchEnum} [..other search criteria] />
                    </Suspense>
                </ErrorBoundary>
            : <span>&nbsp;</span>
            }

如果你只使用myEnum,每当有人输入一个新值时,它就会触发一个渲染,从而触发你的搜索。如果你通过设置当你点击按钮时触发搜索的值来分离输入和搜索,你的用户可以输入输入,但只有点击才会触发搜索。因为只有点击才会改变搜索所监听的值。

xdyibdwo

xdyibdwo3#

大多数答案是这样的--如果不希望组件重新呈现,就不要用useState()设置变量,而是使用useRef()。
因此,现在每个select组件的更改处理程序的定义如下:

const myEnum = useRef(0);
function onChangeMyEnum(myEnumId : number){
    myEnum.current = myEnumId;
}

这是之前我(错误地)调用useState时的样子:

const [myEnum, setMyEnum] = useState(0);
function onChangeMyEnum(myEnumId : number){
    setMyEnum(myEnumId);
}

然后,我将这些变量传递给组件,方法与之前相同:

<div>
    <ErrorBoundary FallbackComponent={MyErrorHandler}>
        <h2>My Search Criteria Select</h2>
        <Suspense fallback={<Spinner/>}>
            <MyEnums onChange={onChangeMyEnum} />
        </Suspense>
    </ErrorBoundary>
</div>

可防止select中的每一个变更都导致元件重新呈现的问题。
按钮单击调用setIsSearch(true),但此状态需要设置为“false”,以便组件在后续按钮单击时重新呈现。为此,请添加对useEffect的调用:

useEffect(() => {
    if(isSearch === true)
    {
        setIsSearch(false);
    }
})

最后一步是将isSearch变量传递到MySearchResults组件中。根据上面的@ZaeemKhaliq答案,我将其添加到了useQuery调用中,这样,除非用户单击了搜索按钮,否则我就不必运行查询了。这是必需的,因为useQuery不能有条件地运行;也就是说,在TemplateSearchResults组件中,我不能简单地在useQuery语句上方放置一个if块,然后在props.isSearch为false时返回。
下面是MySearchResults组件现在的样子:

import  React from 'react';
import { useQuery } from 'react-query';
import TemplateSearchResult from './TemplateSearchResult';

const fetchTemplates = async (carrierGroupId : number, stateId : number, policyTypeId : number, activityStatusCode: string) => {
    let url = `${process.env.REACT_APP_PMSYS_API}/GetTemplates/?carrierGroupId=${carrierGroupId}&stateId=${stateId}&policyTypeId=${policyTypeId}&activityStatusCode=${activityStatusCode}`;
    console.log('fetchTemplates: ' + url)
    const response = await fetch(url);
    return response.json();
}

const TemplateSearchResults = (props : any) => { 
//fyi, you cannot return here if props.IsSearch===false, compiler barks at you. useQuery cannot be called conditionally.

    const {data} = useQuery(['templates', props.carrierGroup,props.state,props.policyType,props.activityStatusCode]
        ,() => fetchTemplates(props.carrierGroup.current,props.state.current,props.policyType.current,props.activityStatusCode.current),
        {
            suspense:true,
            enabled:props.isSearch
        });
       
    return (data === undefined ? <span>No search was done</span> :
        <>
        <table className='table-auto'>
            <thead>
                <tr>
                    <th></th>
                    <th>File Name</th>
                    <th>Groups</th>
                    <th>Types</th>
                    <th>Title</th>
                    <th>Form</th>
                    <th>State</th>
                    <th>Active</th>
                    <th>Required</th>
                    <th>Client Specific</th>
                    <th>Checked Out</th>
                </tr>
            </thead>
            <tbody>
                {data.map((templateSearchResult: {
                    active : boolean ,
                    carrierGroupNumbers: string,
                    checkedOut : string,
                    fileName: string,
                    formType: string,
                    isClientSpecific: boolean,
                    policyTypes: string,
                    required: boolean,
                    stateName: string,
                    templateId:number,
                    title:string}) => <TemplateSearchResult key={templateSearchResult.templateId} 
                        active={templateSearchResult.active} 
                        carrierGroupNumbers={templateSearchResult.carrierGroupNumbers} 
                        checkedOut={templateSearchResult.checkedOut} 
                        fileName={templateSearchResult.fileName} 
                        formType={templateSearchResult.formType} 
                        isClientSpecific={templateSearchResult.isClientSpecific} 
                        policyTypes={templateSearchResult.policyTypes} 
                        required={templateSearchResult.required} 
                        stateName={templateSearchResult.stateName} 
                        templateId={templateSearchResult.templateId} 
                        title={templateSearchResult.title}/>)}
            </tbody>
            </table>
        </>
    );
};

export default TemplateSearchResults;

在我的父搜索页面上,我删除了搜索结果组件呈现周围的“if”块,而是只呈现,传递“isSearch”变量,以便组件可以决定呈现什么:

<div className='w-auto'>
    <ErrorBoundary FallbackComponent={TemplatesErrorHandler}>
        <Suspense fallback={<Spinner/>}>
            <TemplateSearchResults isSearch={isSearch} carrierGroup={carrierGroup} state={state} policyType={policyType} activityStatusCode={activityStatusCode} />
        </Suspense>
    </ErrorBoundary>
</div>

哇。所有这些都让我想起了对核能的旧批评:这是一个非常复杂的生成html的方法。

相关问题