是否可以防止在组件状态更改/重新呈现时重新获取“useLazyQuery”查询?

Is it possible to prevent `useLazyQuery` queries from being re-fetched on component state change / re-render?

目前我有一个 useLazyQuery 钩子,它在按下按钮(搜索表单的一部分)时触发。

挂钩正常运行,仅在按下按钮时触发。但是,一旦我触发它一次,它就会在每次组件重新呈现时触发(通常是由于状态更改)。

因此,如果我搜索一次,然后编辑搜索字段,结果会立即出现,我不必再次点击搜索按钮。

不是我想要的 UI,如果您完全删除搜索文本(因为它试图使用 null 作为变量进行搜索),它会导致错误,有什么方法可以防止useLazyQuery 在重新渲染时被重新获取?

这可以通过使用 useQuery 解决,具体取决于 'searching' 状态,当我单击按钮时该状态会打开。但是我宁愿看看我是否可以避免增加组件的复杂性。

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 文档没有提到 useLazyQuery 是否应该在变量更改时继续触发查询,但是他们确实建议在您想要手动触发时使用 useApolloClient 挂钩一个问题。他们 have an example 匹配此用例(单击按钮触发查询)。

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>
  );
}

您不必将 async 与 apollo 客户端一起使用(您可以,它有效)。但是如果你想使用 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 Client 文档对此没有明确说明,但 useLazyQuery 与 useQuery 一样,首先从缓存中获取。如果查询之间没有变化,则不会使用网络调用重新获取。为了每次都进行网络呼叫,您可以根据您的使用情况将 fetchPolicy 更改为 network-onlycache-and-network (documentation link to the fetchPolicy options)。因此,在您的示例中将 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>
  );
};

使用useLazyQuery时,可以将nextFetchPolicy设置为"standby"。这将防止查询在第一次获取后再次触发。例如,我在购物车上下文中使用钩子在初始加载时从 E-Commerce 后端获取购物车。之后,所有更新都来自购物车的突变。