【问题标题】:Is it possible to prevent `useLazyQuery` queries from being re-fetched on component state change / re-render?是否可以防止在组件状态更改/重新渲染时重新获取“useLazyQuery”查询?
【发布时间】:2019-12-21 07:08:48
【问题描述】:

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

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

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

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

这可以使用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>
  );
};

【问题讨论】:

  • 即使我遇到了这个问题。我要做的是在组件安装期间使用LazyQuery useEffect(() =&gt; refetchFunction(), []) 但是每当我的状态发生变化时,都会再次调用refetchFunction(内部某个地方)

标签: graphql apollo react-hooks react-apollo apollo-client


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    Apollo 客户端文档没有明确说明这一点,但 useLazyQuery 与 useQuery 一样,首先从缓存中获取。如果查询之间没有变化,则不会使用网络调用重新获取。为了每次都进行网络调用,您可以根据您的用例(documentation link to the fetchPolicy options)将fetchPolicy 更改为network-onlycache-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>
      );
    };
    

    【讨论】:

      【解决方案3】:

      您不必将 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>
        );
      }
      

      【讨论】:

      • 在我看来这应该是公认的答案,因为使用的是 useLazyQuery 钩子。
      • 如何找回惰性查询的错误和加载状态?
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2020-04-24
        • 1970-01-01
        • 2019-07-28
        • 1970-01-01
        • 2019-10-06
        • 2020-10-20
        • 1970-01-01
        • 2016-05-26
        相关资源
        最近更新 更多