As we know a react component is re-rendered when it's props or state changes.
Now i'm using useQuery
from react-apollo
package like below:
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;
When i run the code above, we first get Loading...
in DOM which is then updated to list containing query data (once it arrives). But how does react know to re-render my component once data is received from query.
Are these data
, loading
and error
properties mapped to component props and they are updating? If so, why doesn't chrome dev tools show any props for this BookList
component?
Can someone explain how is this useQuery
custom hook working here?
A good way of figuring out (roughly) what is happening in useQuery
is to consider how you'd do it yourself, e.g.
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>
</>
);
};
In the above you can see your component has state (not props) data
, loading
and error
which causes your component to re-render.
You can then imagine this logic was wrapped in your own useQuery
hook:
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>
</>
);
};
So ultimately your component is re-rendering because it does have data
, loading
and error
held in MyComponent
state, it's just abstracted away.