问题描述
目前,我有一个useLazyQuery挂钩,该挂钩在按钮上触发(搜索表格的一部分).
钩子的行为正常,只有在按下按钮时才会发射.但是,一旦我发射一次,每次组件重新租赁时都会被解雇(通常是由于状态更改).
因此,如果我搜索一次,然后编辑搜索字段,结果立即出现,我不必再次单击搜索按钮.
不是我想要的UI,如果您完全删除搜索文本(因为它试图用null作为变量)会导致错误,是否有任何方法可以防止useLazyQuery在重新渲染?
这可以使用useQuery依赖于"搜索"状态的useQuery来解决,该状态在我单击按钮时会在``搜索''状态下使用.但是,我宁愿看看是否可以避免在组件中添加复杂性.
const AddCardSidebar = props => { const [searching, toggleSearching] = useState(false); const [searchParams, setSearchParams] = useState({ name: '' }); const [searchResults, setSearchResults] = useState([]); const [selectedCard, setSelectedCard] = useState(); const [searchCardsQuery, searchCardsQueryResponse] = useLazyQuery(SEARCH_CARDS, { variables: { searchParams }, onCompleted() { setSearchResults(searchCardsQueryResponse.data.searchCards.cards); } }); ... return ( <div> <h1>AddCardSidebar</h1> <div> {searchResults.length !== 0 && searchResults.map(result => { return ( <img key={result.scryfall_id} src={result.image_uris.small} alt={result.name} onClick={() => setSelectedCard(result.scryfall_id)} /> ); })} </div> <form> ... <button type='button' onClick={() => searchCardsQuery()}> Search </button> </form> ... </div> ); };
推荐答案
react-apollo文档不提及变量变化时是否应该继续触发查询,但是当您想手动触发查询时,它们确实建议使用useApolloClient挂钩.他们有一个示例与此用途匹配的示例案例(单击按钮启动查询).
function DelayedQuery() { const [dog, setDog] = useState(null); const client = useApolloClient(); return ( <div> {dog && <img src={dog.displayImage} />} <button onClick={async () => { const { data } = await client.query({ query: GET_DOG_PHOTO, variables: { breed: 'bulldog' }, }); setDog(data.dog); }} > Click me! </button> </div> ); }
其他推荐答案
您不必与Apollo客户端一起使用async(您可以,它可以使用).但是,如果您想使用useLazyQuery,则只需要在onClick上传递变量,而不是直接在uselazyquery呼叫上.
在上面的示例中,解决方案将是:
function DelayedQuery() { const [dog, setDog] = useState(null); const [getDogPhoto] = useLazyQuery(GET_DOG_PHOTO, { onCompleted: data => setDog(data.dog) }) return ( <div> {dog && <img src={dog.displayImage} />} <button onClick={() => getDogPhoto({ variables: { breed: 'bulldog' }})} > Click me! </button> </div> ); }
其他推荐答案
Apollo客户端文档对此并不明确,但是Uselazyquery(例如UseQuery)首先从缓存中获取.如果查询之间没有更改,则不会使用网络调用来撤退.为了每次进行网络调用,您可以根据您的用例将fetchPolicy更改为network-only或cache-and-network( fetchpolicy选项的文档链接).因此,随着您的示例中的network-only更改,它看起来像这样:
const AddCardSidebar = props => { const [searching, toggleSearching] = useState(false); const [searchParams, setSearchParams] = useState({ name: '' }); const [searchResults, setSearchResults] = useState([]); const [selectedCard, setSelectedCard] = useState(); const [searchCardsQuery, searchCardsQueryResponse] = useLazyQuery(SEARCH_CARDS, { variables: { searchParams }, fetchPolicy: 'network-only', //<-- only makes network requests onCompleted() { setSearchResults(searchCardsQueryResponse.data.searchCards.cards); } }); ... return ( <div> <h1>AddCardSidebar</h1> <div> {searchResults.length !== 0 && searchResults.map(result => { return ( <img key={result.scryfall_id} src={result.image_uris.small} alt={result.name} onClick={() => setSelectedCard(result.scryfall_id)} /> ); })} </div> <form> ... <button type='button' onClick={() => searchCardsQuery()}> Search </button> </form> ... </div> ); };
问题描述
Currently I have a useLazyQuery hook which is fired on a button press (part of a search form).
The hook behaves normally, and is only fired when the button is pressed. However, once I've fired it once, it's then fired every time the component re-renders (usually due to state changes).
So if I search once, then edit the search fields, the results appear immediately, and I don't have to click on the search button again.
Not the UI I want, and it causes an error if you delete the search text entirely (as it's trying to search with null as the variable), is there any way to prevent the useLazyQuery from being refetched on re-render?
This can be worked around using useQuery dependent on a 'searching' state which gets toggled on when I click on the button. However I'd rather see if I can avoid adding complexity to the component.
const AddCardSidebar = props => { const [searching, toggleSearching] = useState(false); const [searchParams, setSearchParams] = useState({ name: '' }); const [searchResults, setSearchResults] = useState([]); const [selectedCard, setSelectedCard] = useState(); const [searchCardsQuery, searchCardsQueryResponse] = useLazyQuery(SEARCH_CARDS, { variables: { searchParams }, onCompleted() { setSearchResults(searchCardsQueryResponse.data.searchCards.cards); } }); ... return ( <div> <h1>AddCardSidebar</h1> <div> {searchResults.length !== 0 && searchResults.map(result => { return ( <img key={result.scryfall_id} src={result.image_uris.small} alt={result.name} onClick={() => setSelectedCard(result.scryfall_id)} /> ); })} </div> <form> ... <button type='button' onClick={() => searchCardsQuery()}> Search </button> </form> ... </div> ); };
推荐答案
The react-apollo documentation doesn't mention whether useLazyQuery should continue to fire the query when variables change, however they do suggest using the useApolloClient hook when you want to manually fire a query. They have an example which matches this use case (clicking a button fires the query).
function DelayedQuery() { const [dog, setDog] = useState(null); const client = useApolloClient(); return ( <div> {dog && <img src={dog.displayImage} />} <button onClick={async () => { const { data } = await client.query({ query: GET_DOG_PHOTO, variables: { breed: 'bulldog' }, }); setDog(data.dog); }} > Click me! </button> </div> ); }
其他推荐答案
You don't have to use async with the apollo client (you can, it works). But if you want to use useLazyQuery you just have to pass variables on the onClick and not directly on the useLazyQuery call.
With the above example, the solution would be:
function DelayedQuery() { const [dog, setDog] = useState(null); const [getDogPhoto] = useLazyQuery(GET_DOG_PHOTO, { onCompleted: data => setDog(data.dog) }) return ( <div> {dog && <img src={dog.displayImage} />} <button onClick={() => getDogPhoto({ variables: { breed: 'bulldog' }})} > Click me! </button> </div> ); }
其他推荐答案
The Apollo Client documentation isn't explicit about this, but useLazyQuery, like useQuery, fetches from the cache first. If there is no change between queries, it will not refetch using a network call. In order to make a network call each time, you can change the fetchPolicy to network-only or cache-and-network depending on your use case (documentation link to the fetchPolicy options). So with a fetchPolicy change of network-only in your example, it'd look like this:
const AddCardSidebar = props => { const [searching, toggleSearching] = useState(false); const [searchParams, setSearchParams] = useState({ name: '' }); const [searchResults, setSearchResults] = useState([]); const [selectedCard, setSelectedCard] = useState(); const [searchCardsQuery, searchCardsQueryResponse] = useLazyQuery(SEARCH_CARDS, { variables: { searchParams }, fetchPolicy: 'network-only', //<-- only makes network requests onCompleted() { setSearchResults(searchCardsQueryResponse.data.searchCards.cards); } }); ... return ( <div> <h1>AddCardSidebar</h1> <div> {searchResults.length !== 0 && searchResults.map(result => { return ( <img key={result.scryfall_id} src={result.image_uris.small} alt={result.name} onClick={() => setSelectedCard(result.scryfall_id)} /> ); })} </div> <form> ... <button type='button' onClick={() => searchCardsQuery()}> Search </button> </form> ... </div> ); };