useQuery 和 useEffect 用于简单搜索
useQuery with useEffect for simple search
我有一个非常复杂的组件,现在我正在尝试实现一个搜索,用户可以在其中键入内容并过滤结果。
// query
const GET_ACCOUNTS = gql`
query accounts{
accounts{
id
name
status
company{
id
name
}
}
}
`;
// get query
const { loading } = useQuery(GET_ACCOUNTS, {
fetchPolicy: "no-cache",
skip: userType !== 'OS_ADMIN',
onCompleted: setSearchResults
});
// example of query result (more than 1)
{
"accounts": [
{
"id": "5deed7df947204960f286010",
"name": "Acme Test Account",
"status": "active",
"company": {
"id": "5de84532ce5373afe23a05c8",
"name": "Acme Inc.",
"__typename": "Company"
},
"__typename": "Account"
},
]
}
// states
const [searchTerm, setSearchTerm] = useState('');
const [searchResults, setSearchResults] = useState([]);
// code to render
<FormControl fullWidth>
<InputLabel htmlFor="seach">Search for accounts</InputLabel>
<Input
id="search"
aria-describedby="Search for accounts"
startAdornment={<InputAdornment position="start"><SearchIcon /></InputAdornment>}
value={searchTerm}
onChange={handleChange}
/>
</FormControl>
{searchResults && searchResults.accounts &&
searchResults.accounts.map(c => {
return (
<>
<ListItem
dense
button
className={classnames({ [classes.selectedAccountContext]: c.id === accountContextId })}
key={c.id}
onClick={() => accountClicked(c.id)}
>
<ListItemText
primary={c.name}
secondary={
<>
<span>{c.company.name}</span>
<span className="d-flex align-items-center top-margin-tiny">
<Badge
color={c.status === 'active' ? "success" : "danger"}
style={{ marginBottom: 0 }}
>
{c.status.replace(/^\w/, c => c.toUpperCase())}
</Badge>
<span className='ml-auto'>
<SvgIcon><path d={mdiMapMarkerRadius} /></SvgIcon>
<SMARTCompanyIcon />
</span>
</span>
</>
}
/>
</ListItem>
</>
)
})
}
// useEffect
useEffect(() => {
if (searchTerm) {
const results = searchResults.accounts.filter((c => c.name.toLowerCase().includes(searchTerm)))
setSearchResults(results)
}
}, [searchTerm])
问题是当我开始在我的搜索字段中输入时,我正在查看我的 searchResults
,当我输入一个字符时它被过滤了,但是当我输入下一个字符时它会中断。
TypeError: Cannot read property 'filter' of undefined
即使我输入了一个字母,它也不会呈现在视图上。
根据您的数据,searchResults
的初始值是具有 accounts
键的字典。但是当你在 useEffect
部分更新它时,它会变成一个列表:
useEffect(() => {
if (searchTerm) {
const results = searchResults.accounts.filter((c => c.name.toLowerCase().includes(searchTerm)))
// This changes the value of searchResults to an array
setSearchResults(results)
}
}, [searchTerm])
当setSearchResults
在useEffect
中被调用时,searchResults
的值从对象变为数组:
来自这个:
搜索结果 = {
账户:[
...
]
}
对此:
搜索结果=[
...
]
这就是为什么它在第一次搜索后引发 TypeError: Cannot read property 'filter' of undefined
,因为不再有 accounts
键。
要解决此问题,您需要在 searchResults
的数据类型上保持一致,最好首先将其设为列表。您可以在 onCompleted
部分执行此操作:
const { loading } = useQuery(GET_ACCOUNTS, {
fetchPolicy: "no-cache",
skip: userType !== 'OS_ADMIN',
onCompleted: (data) => setSearchResults(data.accounts || [])
});
请注意,我们将 searchResults
设置为 accounts
值。之后,您还需要了解如何访问 searchResults
{searchResults &&
searchResults.map(c => {
return (
...renderhere
)
})
}
而你的 useEffect
将是这样的:
useEffect(() => {
if (searchTerm) {
const results = searchResults.filter((c => c.name.toLowerCase().includes(searchTerm)))
setSearchResults(results)
}
}, [searchTerm])
提示:
您可能需要将 searchResults
重命名为 accounts
以使其更清晰。另请注意,在第一次搜索后,您的选项将仅限于之前的搜索结果,因此您可能还想将所有帐户存储在不同的变量中:
const [allAccounts, setAllAccounts] = useState([])
const [searchedAccounts, setSearchedAccounts] = useState([])
// useQuery
const { loading } = useQuery(GET_ACCOUNTS, {
fetchPolicy: "no-cache",
skip: userType !== 'OS_ADMIN',
onCompleted: (data) => {
setAllAccounts(data.accounts || [])
setSearchedAccounts(data.accounts || [])
}
});
// useEffect
useEffect(() => {
if (searchTerm) {
// Notice we always search from allAccounts
const results = allAccounts.filter((c => c.name.toLowerCase().includes(searchTerm)))
setSearchedAccounts(results)
}
}, [searchTerm])
我有一个非常复杂的组件,现在我正在尝试实现一个搜索,用户可以在其中键入内容并过滤结果。
// query
const GET_ACCOUNTS = gql`
query accounts{
accounts{
id
name
status
company{
id
name
}
}
}
`;
// get query
const { loading } = useQuery(GET_ACCOUNTS, {
fetchPolicy: "no-cache",
skip: userType !== 'OS_ADMIN',
onCompleted: setSearchResults
});
// example of query result (more than 1)
{
"accounts": [
{
"id": "5deed7df947204960f286010",
"name": "Acme Test Account",
"status": "active",
"company": {
"id": "5de84532ce5373afe23a05c8",
"name": "Acme Inc.",
"__typename": "Company"
},
"__typename": "Account"
},
]
}
// states
const [searchTerm, setSearchTerm] = useState('');
const [searchResults, setSearchResults] = useState([]);
// code to render
<FormControl fullWidth>
<InputLabel htmlFor="seach">Search for accounts</InputLabel>
<Input
id="search"
aria-describedby="Search for accounts"
startAdornment={<InputAdornment position="start"><SearchIcon /></InputAdornment>}
value={searchTerm}
onChange={handleChange}
/>
</FormControl>
{searchResults && searchResults.accounts &&
searchResults.accounts.map(c => {
return (
<>
<ListItem
dense
button
className={classnames({ [classes.selectedAccountContext]: c.id === accountContextId })}
key={c.id}
onClick={() => accountClicked(c.id)}
>
<ListItemText
primary={c.name}
secondary={
<>
<span>{c.company.name}</span>
<span className="d-flex align-items-center top-margin-tiny">
<Badge
color={c.status === 'active' ? "success" : "danger"}
style={{ marginBottom: 0 }}
>
{c.status.replace(/^\w/, c => c.toUpperCase())}
</Badge>
<span className='ml-auto'>
<SvgIcon><path d={mdiMapMarkerRadius} /></SvgIcon>
<SMARTCompanyIcon />
</span>
</span>
</>
}
/>
</ListItem>
</>
)
})
}
// useEffect
useEffect(() => {
if (searchTerm) {
const results = searchResults.accounts.filter((c => c.name.toLowerCase().includes(searchTerm)))
setSearchResults(results)
}
}, [searchTerm])
问题是当我开始在我的搜索字段中输入时,我正在查看我的 searchResults
,当我输入一个字符时它被过滤了,但是当我输入下一个字符时它会中断。
TypeError: Cannot read property 'filter' of undefined
即使我输入了一个字母,它也不会呈现在视图上。
根据您的数据,searchResults
的初始值是具有 accounts
键的字典。但是当你在 useEffect
部分更新它时,它会变成一个列表:
useEffect(() => {
if (searchTerm) {
const results = searchResults.accounts.filter((c => c.name.toLowerCase().includes(searchTerm)))
// This changes the value of searchResults to an array
setSearchResults(results)
}
}, [searchTerm])
当setSearchResults
在useEffect
中被调用时,searchResults
的值从对象变为数组:
来自这个:
搜索结果 = { 账户:[ ... ] }
对此:
搜索结果=[ ... ]
这就是为什么它在第一次搜索后引发 TypeError: Cannot read property 'filter' of undefined
,因为不再有 accounts
键。
要解决此问题,您需要在 searchResults
的数据类型上保持一致,最好首先将其设为列表。您可以在 onCompleted
部分执行此操作:
const { loading } = useQuery(GET_ACCOUNTS, {
fetchPolicy: "no-cache",
skip: userType !== 'OS_ADMIN',
onCompleted: (data) => setSearchResults(data.accounts || [])
});
请注意,我们将 searchResults
设置为 accounts
值。之后,您还需要了解如何访问 searchResults
{searchResults &&
searchResults.map(c => {
return (
...renderhere
)
})
}
而你的 useEffect
将是这样的:
useEffect(() => {
if (searchTerm) {
const results = searchResults.filter((c => c.name.toLowerCase().includes(searchTerm)))
setSearchResults(results)
}
}, [searchTerm])
提示:
您可能需要将 searchResults
重命名为 accounts
以使其更清晰。另请注意,在第一次搜索后,您的选项将仅限于之前的搜索结果,因此您可能还想将所有帐户存储在不同的变量中:
const [allAccounts, setAllAccounts] = useState([])
const [searchedAccounts, setSearchedAccounts] = useState([])
// useQuery
const { loading } = useQuery(GET_ACCOUNTS, {
fetchPolicy: "no-cache",
skip: userType !== 'OS_ADMIN',
onCompleted: (data) => {
setAllAccounts(data.accounts || [])
setSearchedAccounts(data.accounts || [])
}
});
// useEffect
useEffect(() => {
if (searchTerm) {
// Notice we always search from allAccounts
const results = allAccounts.filter((c => c.name.toLowerCase().includes(searchTerm)))
setSearchedAccounts(results)
}
}, [searchTerm])