为什么 useQuery 调用会导致我的组件重新渲染?

V S*_*S X 1 javascript apollo reactjs react-apollo react-hooks

正如我们所知,当 props 或 state 发生变化时,react 组件会重新渲染。

我现在正在使用useQueryreact-apollo包象下面这样:

import { gql, useQuery } from '@apollo/client';

const getBookQuery = gql`
  {
    books {
      name
    }
  }
`;

function BookList() {
    const { loading, error, data} = useQuery(getBookQuery);

    if(loading) return <p>Loading....</p>
    if(error) return <p>Ops! Something went wrong</p>

    return (
      <>
        <ul>
          {data.books.map(book => (
            <li key={book.name}>{book.name}</li>
          ))}
        </ul>
      </>
    )
}

export default BookList;
Run Code Online (Sandbox Code Playgroud)

当我运行上面的代码时,我们首先进入Loading...DOM,然后将其更新为包含查询数据的列表(一旦它到达)。但是一旦从查询中收到数据,react 如何知道重新渲染我的组件。

这些data,loadingerror属性是否映射到组件 props 并且它们正在更新?如果是这样,为什么 chrome 开发工具不显示此BookList组件的任何道具?

在此处输入图片说明

有人可以解释一下这个useQuery自定义钩子是如何在这里工作的吗?

ris*_*ott 6

弄清楚(大致)正在发生的事情的一个好方法useQuery是考虑如何自己做,例如

const MyComponent = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(async () => {
    try {
      setLoading(true);
      const data = await GraphQL.request(getBookQuery);
      setData(data);
    } catch (ex) {
      setError(ex);
    } finally {
      setLoading(false);
    }
  }, []);

  if(loading) return <p>Loading....</p>
  if(error) return <p>Ops! Something went wrong</p>

  return (
    <>
      <ul>
        {data.books.map(book => (
          <li key={book.name}>{book.name}</li>
        ))}
      </ul>
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

在上面你可以看到你的组件有状态(没有道具)dataloadingerror这将导致您的组件重新呈现。

然后你可以想象这个逻辑被包裹在你自己的useQuery钩子中:

const useQuery = (query, variables) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(async () => {
    try {
      setLoading(true);
      const data = await GraphQL.request(query, variables);
      setData(data);
    } catch (ex) {
      setError(ex);
    } finally {
      setLoading(false);
    }
  }, []);

  return { data, loading, error };
}

const MyComponent = () => {
  const { data, loading, error } = useQuery(getBookQuery);

  if(loading) return <p>Loading....</p>
  if(error) return <p>Ops! Something went wrong</p>

  return (
    <>
      <ul>
        {data.books.map(book => (
          <li key={book.name}>{book.name}</li>
        ))}
      </ul>
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

所以最终你的组件正在重新渲染,因为它确实有dataloading并且error保持在MyComponent状态中,它只是被抽象掉了。

  • 是的,实际上不存在“钩子”状态,只有组件状态,自定义钩子仅定义与其调用的组件关联的状态。事实上 useState 在不同的函数 (useQuery) 中被调用在很大程度上是没有意义的,它都与调用它的组件有关。 (6认同)
  • 啊,那么基本上钩子的状态变量也可以用作使用钩子的组件的状态(或道具?)?如果钩子的状态发生变化,组件渲染也会被触发。使用非常基本的自定义钩子对其进行了测试,看起来实际上就是这样的情况`https://codesandbox.io/s/modest-sea-ghqjm`。 (3认同)