Я пытаюсь внедрить функцию поиска в свою систему управления, используя React-Hooks и клиент GraphQL-Apollo. Хотя интерфейс отображается успешно, когда я нажимаю кнопку «Поиск», выдается ошибка с именем:
Invalid hook call. Hooks can only be called inside of the body of a function component.
Я почти уверен, что useQuery вызывается внутри функции, поэтому я не понимаю, что это вызовет. Другие функции, такие как отображение всех пользователей и добавление новых пользователей, работают нормально.
Я пробовал несколько способов реализовать функцию поиска и искать в Интернете, но все еще не мог решить ее. Я тоже впервые сталкиваюсь с React-Hooks.
Вот мой текущий код в компоненте searchUser
import React from 'react'
import {useQuery} from 'react-apollo-hooks';
import {searchUserQ} from '../queries/queries';
const SearchUserForm = () => {
let name = '';
let content;
return (
<div id = "edit-user">
<div className = "field">
<label htmlFor = "name">Search UserName</label>
<input type = "text" id = "name" onChange = { (event) => {
name = event.target.value }}/>
<button onClick = {(event) => {
event.preventDefault();
const { data, loading, error } = useQuery(searchUserQ, {
variables: { name: name },
suspend: false
});
if (loading) {
content = <p>Loading User...</p>
}
if (error){
console.info(`Error Occur: ${ error }`);
content = <p>Error Occur!</p>
}
content = data.users.map(user => (
<p key = {user.id}>Username: {user.name} ID: {user.id}</p>
));
}}>
Submit
</button>
<p>{ content }</p>
</div>
</div>
)
}
export default SearchUserForm;
Может кто-нибудь помочь с этим?
Еще один вопрос заключается в том, что мой data, похоже, возвращает undefined каждый раз, когда я выполняю запрос. Что-то не так с моим запросом?
Вот запрос:
const searchUserQ = gql`
query User($name: String!){
user(name: $name){
id
name
email
}
}
`;
Спасибо и признателен за помощь!





Согласно Правила хуков:
Don’t call Hooks from regular JavaScript functions. Instead, you can:
✅ Call Hooks from React function components.
✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).
Если вам нужно вручную вызвать запрос вручную в ответ на событие, используйте клиент Apollo напрямую. Вы можете использовать useApolloClient, чтобы получить экземпляр клиента внутри вашего компонента:
const SearchUserForm = () => {
const client = useApolloClient();
...
return (
...
<button onClick = {async (event) => {
try {
const { data } = client.query({
query: searchUserQ,
variables: { name: event.target.value },
});
// Do something with data, like set state
catch (e) {
// Handle errors
}
}} />
Вы также можете использовать useQuery, например:
const SearchUserForm = () => {
const [name, setName] = useState('')
const { data, loading, error } = useQuery(searchUserQ, {
variables: { name },
});
...
return (
...
<button onClick = {async (event) => {
setName(event.target.value)
...
}} />
Я нашел ответ на свой второй вопрос, спасибо за помощь!
Я обнаружил проблему со своим вторым ответом, нужно просто обернуть оператор if перед его выполнением, вот полный код:
import React, {useState} from 'react'
import {useQuery} from 'react-apollo-hooks';
import {searchUserQ} from '../queries/queries';
const SearchUserForm = () => {
const [name, setName] = useState('');
const { data, error, loading } = useQuery(searchUserQ, {
variables: { name }
});
let content;
let sName;
if (data.user !== undefined && data.user !== null) {
if (loading) { content = 'Loading User...' }
if (error) { content = `Error Occur: ${error}` }
const user = data.user;
content = `Username: ${ user.name } ID: ${ user.id }`
}
return (
<div id = "edit-user">
<div className = "field">
<label htmlFor = "name">Search UserName</label>
<input type = "text" id = "name" onChange = {(event) => {
sName = event.target.value;
}}/>
<button onClick = {(event) => {
setName(sName);
}} value = {name}>
Search
</button>
</div>
<div>
<p>{ content }</p>
</div>
</div>
)
};
export default SearchUserForm;
Вы можете использовать метод использоватьLazyQuery и предоставить свой объект данных всему компоненту.
import {useLazyQuery} from '@apollo/react-hooks';
// - etc. -
const SearchUserForm = () => {
// note: keep the query hook on the top level in the component
const [runQuery, { data, loading, error }] = useLazyQuery(searchUserQ);
// you can now just use the data as you would regularly do:
if (data) {
console.info(data);
}
return (
<div id = "edit-user">
<div className = "field">
<label htmlFor = "name">Search UserName</label>
<input
type = "text"
id = "name"
onChange = {(event) => {name = event.target.value }} />
<button onClick = {
(event) => {
event.preventDefault();
// query is executed here
runQuery({
variables: { name }, // note: name = property shorthand
suspend: false
})
}}>
// - etc. -
);
}
В отличие от использовать запрос, метод использоватьLazyQuery будет выполняться только по клику.
В тот момент, когда вы можете передать значение «имя» в качестве параметра.
Если, например, вместо этого вы будете использовать использовать запрос и у вас есть параметр, который требуется в вашем запросе (например, String!), метод useQuery выдаст вам ошибку. Потому что при рендеринге компонента он попытается запустить этот запрос напрямую без обязательного параметра, потому что в этот период времени он еще недоступен.
Спасибо за помощь! Я избавился от ошибки уже! Еще один вопрос: мои «данные» всегда не определены, когда я выполняю запрос, что-то не так с моим запросом? Я обновил свой запрос в вопросе, спасибо!