我已经创建了一个应用程序,使用Rick and Morty API来显示字符列表。此外,此应用程序应该能够实时过滤结果网格。此应用程序还包括InfiniteFiltering
功能。然而,我在这里遇到了几个问题。
让我给予你一些背景。
下面是charactersSlice
文件:
import { createSlice, PayloadAction, createAsyncThunk } from '@reduxjs/toolkit';
import { RootState } from '../../app/store';
import axios from 'axios';
export interface Character {
id: number;
name: string;
species: string;
status: string;
origin: {
name: string;
};
image: string;
}
interface CharactersState {
characters: Character[];
loading: boolean;
error: string | null;
}
const initialState: CharactersState = {
characters: [],
loading: false,
error: null,
};
export const fetchCharacters = createAsyncThunk<Character[], number>(
'characters/fetchCharacters',
async (page) => {
const response = await axios.get(`https://rickandmortyapi.com/api/character/?page=${page}`);
return response.data.results;
}
);
const charactersSlice = createSlice({
name: 'characters',
initialState,
reducers: {
setCharacters: (state, action: PayloadAction<Character[]>) => {
state.characters = action.payload;
},
setLoading: (state, action: PayloadAction<boolean>) => {
state.loading = action.payload;
},
setError: (state, action: PayloadAction<string | null>) => {
state.error = action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchCharacters.fulfilled, (state, action) => {
state.characters = action.payload;
state.loading = false;
state.error = null;
})
.addCase(fetchCharacters.pending, (state) => {
state.loading = true;
})
.addCase(fetchCharacters.rejected, (state, action) => {
state.error = action.error.message || 'An error occurred.';
state.loading = false;
});
},
});
export const { setCharacters, setLoading, setError } = charactersSlice.actions;
export const selectCharacters = (state: RootState) => state.characters.characters;
export const selectLoading = (state: RootState) => state.characters.loading;
export const selectError = (state: RootState) => state.characters.error;
export default charactersSlice.reducer;
字符串
这是charactersGrid文件:
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchCharacters, selectCharacters } from './charactersSlice';
import { AppDispatch } from '../../app/store';
import CharacterCard from './Character';
import CharacterSearch from './CharactersSearch';
import { Character } from './charactersSlice';
import InfiniteScroll from 'react-infinite-scroll-component';
const CharacterGrid: React.FC = () => {
const characters = useSelector(selectCharacters);
const dispatch: AppDispatch = useDispatch();
const [searchTerm, setSearchTerm] = useState<string>('');
const [page, setPage] = useState(1);
const [allCharacters, setAllCharacters] = useState<Character[]>([]);
useEffect(() => {
dispatch(fetchCharacters(page));
}, [dispatch, page]);
useEffect(() => {
// Append newly loaded characters to the existing array
setAllCharacters((prevCharacters) => [...prevCharacters, ...characters]);
}, [characters, page]);
const filteredCharacters = allCharacters.filter((character) =>
character.name.toLowerCase().includes(searchTerm.toLowerCase())
);
const loadMore = () => {
if (searchTerm === '') {
setPage(page + 1);
}
};
return (
<div className="character-grid-container">
<CharacterSearch searchTerm={searchTerm} onSearchChange={setSearchTerm} />
<div className="character-grid-title-container">
<hr />
<h1 className="character-grid-title">Lista de personajes</h1>
<hr />
</div>
<InfiniteScroll
dataLength={filteredCharacters.length}
next={loadMore}
hasMore={searchTerm === ''} // Only allow loading more if searchTerm is empty
loader={
searchTerm === '' ? ( // Check if searchTerm is empty
<h4 className="notification">Loading...</h4>
) : null // If searchTerm is not empty, don't render anything
}
>
<div className="character-grid">
{filteredCharacters.map((character) => (
<CharacterCard key={character.id} character={character} />
))}
</div>
</InfiniteScroll>
</div>
);
};
export default CharacterGrid;
型
现在正在发生的事情:当我向下滚动时,无限滚动正常工作。但是,如果我尝试在搜索字段中写入内容,生命过滤不起作用。此外,如果我再次清除搜索字段,它会再次加载新的更多字符(这不应该发生)。
如果用户滚动,它应该只向allCharacters
数组添加新字符。此外,如果搜索字段为某个值,它不应该加载更多字符,如果它再次为空,它也不应该加载更多字符。
目标是过滤allCharacters
数组中的当前可用字符。
显然,过滤器只有在我在下面的代码中使用characters.filter
而不是allCharacters.filter
时才有效:
const filteredCharacters = allCharacters.filter((character) =>
character.name.toLowerCase().includes(searchTerm.toLowerCase())
);
型
求你了,我需要帮助来修复它
我尝试了不同的方法,但它不工作。
该应用程序应该能够加载更多的字符只有当它滚动和搜索字段为空。
更新答案:
非常感谢您的建议。
在发布了前面的代码之后,我用一些代码更新了charactersGrid,以避免密钥重复并生成随机ID。
你能帮助我将以前的解决方案应用到这个更新的代码吗?
// Import necessary dependencies and components from external files
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchCharacters, selectCharacters, selectError } from './charactersSlice';
import { AppDispatch } from '../../app/store';
import CharacterCard from './Character';
import CharacterSearch from './CharactersSearch';
import { Character } from './charactersSlice';
import InfiniteScroll from 'react-infinite-scroll-component';
// Define a functional React component called CharacterGrid
const CharacterGrid: React.FC = () => {
// Retrieve characters and error from the Redux store
const characters = useSelector(selectCharacters);
const error = useSelector(selectError);
const dispatch: AppDispatch = useDispatch();
// Define and initialize state variables for search, pagination, and character data
const [searchTerm, setSearchTerm] = useState<string>('');
const [page, setPage] = useState(1);
const [allCharacters, setAllCharacters] = useState<Character[]>([]);
// Use the useEffect hook to fetch characters when the page or store changes
useEffect(() => {
dispatch(fetchCharacters(page));
}, [dispatch, page]);
// Use another useEffect to append newly loaded characters to the existing character data
useEffect(() => {
// Create a set of character IDs to check for duplicates
const existingCharacterIds = allCharacters.map((character) => character.id);
const newCharacters = characters.filter(
(character) => !existingCharacterIds.includes(character.id)
);
// Append newly loaded characters to the existing array
setAllCharacters((prevCharacters) => [...prevCharacters, ...newCharacters]);
}, [characters, allCharacters, page]);
// Filter characters based on the search term, and assign random keys to each character
const filteredCharacters = allCharacters.filter((character) =>
character.name.toLowerCase().includes(searchTerm.toLowerCase())
);
// Add a random key to the character
const filteredCharactersWithRandomKeys = filteredCharacters.map((character) => {
const randomKey = Math.random().toString(36).substring(7);
return { ...character, randomKey };
});
// Function to load more characters when the InfiniteScroll component triggers it
const loadMore = () => {
if (searchTerm === '') {
setPage(page + 1);
}
};
// Render the component's JSX structure
return (
<div id="character-grid-container" className="character-grid-container">
{/* Display the character search input component */}
<CharacterSearch searchTerm={searchTerm} onSearchChange={setSearchTerm} />
<div className="character-grid-title-container">
<hr />
<h1 className="character-grid-title">Lista de personajes</h1>
<hr />
</div>
{/* Implement the InfiniteScroll component for lazy loading */}
<InfiniteScroll
dataLength={filteredCharactersWithRandomKeys.length}
next={loadMore}
hasMore={searchTerm === ''} // Only allow loading more if searchTerm is empty
loader={
searchTerm === '' ? ( // Check if searchTerm is empty
error ? ( // If there's an error, display the error message
<h4 className="notification error">{error}</h4>
) : (
<h4 className="notification">Loading...</h4>
)
) : null // If searchTerm is not empty, don't render anything
}
>
{filteredCharactersWithRandomKeys.length === 0 ? (
<h4 className="notification">No se ha encontrado ningún personaje...</h4>
) : (
<div className="character-grid">
{/* Map and render the CharacterCard component for each character */}
{filteredCharactersWithRandomKeys.map((character) => (
<CharacterCard key={character.randomKey} character={character} />
))}
</div>
)}
</InfiniteScroll>
</div>
);
};
// Export the CharacterGrid component as the default export
export default CharacterGrid;
型
1条答案
按热度按时间xghobddn1#
characters
数组中。useEffect
钩子为同一页发起重复调用,在allCharacters
数组中创建重复。您可以直接从存储中选择的characters
状态和本地searchTerm
筛选器状态值计算filteredCharacters
。character.slice
字符串
企业简介
型
x1c 0d1x的数据
注意事项/建议
useEffect
钩子在React.StrictMode
组件的开发版本中运行得更频繁。StrictMode
在沙箱中被注解掉,因此不会进行重复的API调用。characters
数组,而是将数据保存在Map或Set中(* 不允许数据重复 *),并使用selectCharacters
将状态转换为UI的数组。