Я делаю проект библиотеки (см. ссылку для получения дополнительной информации) с react
, redux-toolkit
и typescript
.
Я делаю функционал addToWishlist
: пользователь просто нажимает на звездочку, код отправляет действие, которое исправляет базу данных, и состояние обновляется. Есть еще одно действие по удалению книги из списка.
В компоненте <Wishlist />
я, конечно, хочу отобразить заголовки и другие данные каждой книги. Итак, я useAppSelector
извлекаю wishlist
и все list
книг, и с помощью filter
и map
я получаю соответствующие книги, но я не могу их отобразить, потому что это дает мне ts
ошибку: index.d.ts(1373, 9): The expected type comes from property 'children' which is declared here on type 'DetailedHTMLProps<HTMLAttributes<HTMLUListElement>, HTMLUListElement>'
Важное замечание о структуре
wishlist
— это массив строк, представляющих собой идентификаторы книг.
Вместо этого весь list
книг представляет собой массив объектов: каждый объект представляет собой книгу с названием, автором, идентификатором и т. д.
Мои вопросы:
Что здесь происходит не так?
В принципе, лучше ли сохранять идентификаторы в массиве wishlist
(как я сделал), поэтому мне нужно получить данные книг в моем компоненте <Wishlist />
с помощью filter
и map
, или лучше сохранить всю книгу ( или, по крайней мере, информацию, которая мне нужна), чтобы было легче иметь данные?
Как лучше всего отображать список в первом случае, когда мне нужно извлекать данные с помощью filter
и map
?
Вот попытка:
const Wishlist = () => {
const dispatch = useAppDispatch();
const wishlist = useAppSelector(state => state.user.userInfo.wishlist);
const list = useAppSelector(state => state.catalogue.list);
const renderBooks = () => {
return wishlist.map(wishId => {
list.filter(book => {
if (book.id === wishId) {
return (
<li>{book.title}</li>
)
}
})
})
}
return (
<ul>
{renderBooks()}
</ul>
)
};
export default Wishlist;
Отредактировал - может проблема не в конкатенации методов, а что-то в вызове функции?
Еще один небольшой пример с другим решением, но с той же проблемой
interface fakeObj {
id: string;
name: string;
}
const fakeIds = ['5', '12', '878'] // this is my 'wishlist', containing only ids
const fakeList: fakeObj[] = [ // this is my 'list', containing objects, which were single books
{ id: '334', name: 'Tony' },
{ id: '5', name: 'Milly' },
{ id: '12', name: 'Jack' },
{ id: '7', name: 'Stew' },
{ id: '878', name: 'Mark' },
{ id: '87998', name: 'Greg' }
]
const renderArray = () => {
const arr: fakeObj[] = [];
fakeIds.forEach(id => {
fakeList.filter(obj => {
if (obj.id === id) {
arr.push(obj)
}
})
})
return arr; // the returned array is what I want: an array continaing the objects that i need
}
return (
<ul>
{/* The problem actually is here! */}
{renderArray()}
</ul>
)
Я редактирую свой ответ, чтобы сделать его более точным в отношении структур, но ваш метод в моем случае не работает, и я не уверен в логике. Я сделаю также пример, чтобы понять лучше.
Вы неправильно используете .filter() или .map(), что и вызывает ошибки.
Комментарий выше, который вы отвергли, был правильным решением этой проблемы.
Добавленный отредактированный пример еще более сбивает с толку, потому что вы больше не пытаетесь вернуть отображаемые элементы, поскольку возвращаемый результат представляет собой массив объектов, которые не могут отображаться в UL.
Итак, я возвращаюсь к исходному примеру.
Проблема №1:
Вы пытаетесь вернуть элемент JSX в обратном вызове, переданном в .filter(). Это не то, как работает .filter(). Вы должны передать обратный вызов .filter(), который вернет TRUE или FALSE в зависимости от того, хотите ли вы, чтобы элемент был отфильтрован или отфильтрован. Например. listOfCounts.filter( count => count > 5);
вернет измененный массив всех элементов в listOfCounts, которые больше 5.
Проблема №2:
В настоящее время вы сначала вызываете .map(), что неправильно, но затем также вызываете .filter() ВНУТРИ .map() вместо того, чтобы связывать карту ПОСЛЕ фильтра. Потому что прямо сейчас ваш вызов .map() даже не пытается вернуть список элементов JSX, представляющих названия книг, которые вы хотите отобразить. Эти вызовы должны быть связаны противоположно тому, как вы это делаете. Пример очень простого использования .map():
listOfNames.map(name => <div>{name}</div>);
Простой пример объединения .map() с .filter():
listOfNames.filter(name => name[0] == 'J').map(name => <div>{name}</div>);
который отфильтровывает все имена, которые не начинаются с прописной буквы J.
Решение выше, которое г-н Рафик предоставил вам, является правильным/очень близким к правильному в зависимости от точной реализации, поэтому я предлагаю вам объединить эту добавленную информацию с его решением.
Спасибо. На самом деле, проблема была (также) в том, что я попробовал решение Rafique, но renderBooks() все еще был подписан как ошибка TS, и я не могу отобразить результат, потому что, я думаю, я не указываю тип возвращаемого значения в функция. Затем я пытаюсь поместить код прямо в возврат, и это работает.
Первое, что вам нужно понять, когда использовать карту и фильтр и каков тип возвращаемого значения каждого метода. Вы хотите отобразить все книги с идентификаторами в
wishlist
. Сначала вам нужно отфильтровать книги, которые содержат идентификатор в списке желаний, используя фильтр (который возвращает книгу, если она удовлетворяет условию), а затем использовать карту.list.filter((book)=> wishlist.includes(book.id)).map((book)=> <li key = {book.id}>{book.title}</li>)
Документация по методам массива