Как создать фильтр для продуктов на React?
Есть компоненты React
Main.jsx
import React from "react";
import { Products } from "../Products";
import { Filter } from "../Filter";
import { Product } from "../Product";
class Main extends React.Component {
state = {
products: [],
};
componentDidMount() {
fetch("./products.json")
.then((responce) => responce.json())
.then((data) => this.setState({ products: Object.values(data) }));
}
productFilter = (type = "all") => {
const selected = [];
return this.state.products.map((product) => {
if (product.prod_status.contains(type)) {
selected.push(product);
}
});
};
render() {
const { products } = this.state;
return (
<main className="container content">
<Filter />
<Products products={products} />
</main>
);
}
}
export { Main };
Filter.jsx
import React from "react";
class Filter extends React.Component {
state = {
type: "all",
};
hadleFilter = (event) => {
this.setState({ type: event.target.dataset.type }, () => {
this.props.productFilter(this.state.type);
});
};
render() {
return (
<div clasName="row">
<div className="sort_container">
<h3 className="sort_title">Sortować według:</h3>
<label>
<input
className="with-gap"
name="prod-status"
type="radio"
data-type="all"
onChange={this.hadleFilter}
checked={this.state.type === "all"}
/>
<span>All</span>
</label>
<label>
<input
className="with-gap"
name="prod-status"
type="radio"
data-type="recommended"
onChange={this.hadleFilter}
checked={this.state.type === "recommended"}
/>
<span>Recommended</span>
</label>
<label>
<input
className="with-gap"
name="prod-status"
type="radio"
data-type="saleout"
onChange={this.hadleFilter}
checked={this.state.type === "saleout"}
/>
<span>Saleout</span>
</label>
<label>
<input
className="with-gap"
name="prod-status"
type="radio"
data-type="bestseller"
onChange={this.hadleFilter}
checked={this.state.type === "bestseller"}
/>
<span>Bestseller</span>
</label>
<label>
<input
className="with-gap"
name="prod-status"
type="radio"
data-type="promotion"
onChange={this.hadleFilter}
checked={this.state.type === "promotion"}
/>
<span>Rromotion</span>
</label>
<label>
<input
className="with-gap"
name="prod-status"
type="radio"
data-type="new"
onChange={this.hadleFilter}
checked={this.state.type === "new"}
/>
<span>New</span>
</label>
</div>
</div>
);
}
}
export { Filter };
Cсылка на весь репозиторий - тут
Вопрос- попытался сделать функцию productFilter() в Main.jsx, которую передаю в Filter.jsx для реализации фильтра после нажатия на любой radio. Ясное дело, что функция написана не правильно, прошу помочь исправить ошибки и довести фильтр в рабочее состояние
Ответы (1 шт):
Автор решения: Yakov Botov
→ Ссылка
Предлагаю следующее решение
(Я немного упростил кол-во фильтров и набор данных для удобства работы с примером, а также переписал на функциональные компоненты, однако суть задачи и ее решения от этого совершенно не изменилась)
const {useState, useCallback, useMemo} = React;
const data = [
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"completed": false
},
{
"userId": 1,
"id": 3,
"title": "fugiat veniam minus",
"completed": false
},
{
"userId": 1,
"id": 4,
"title": "et porro tempora",
"completed": true
},
{
"userId": 1,
"id": 5,
"title": "laboriosam mollitia et enim quasi adipisci quia provident illum",
"completed": false
},
{
"userId": 1,
"id": 6,
"title": "qui ullam ratione quibusdam voluptatem quia omnis",
"completed": false
},
{
"userId": 1,
"id": 7,
"title": "illo expedita consequatur quia in",
"completed": false
},
{
"userId": 1,
"id": 8,
"title": "quo adipisci enim quam ut ab",
"completed": true
},
{
"userId": 1,
"id": 9,
"title": "molestiae perspiciatis ipsa",
"completed": false
},
{
"userId": 1,
"id": 10,
"title": "illo est ratione doloremque quia maiores aut",
"completed": true
}
]
const COMPLETE_FILTER = {
Completed: "completed",
Incompleted: "incompleted",
All: "all"
};
const TodoItem = ({ title, completed }) => {
return (
<article className={"todoItem"}>
<h4>{title}</h4>
<div>
completed:{" "}
<span className={completed ? "completed" : "incompleted"}>
{String(completed)}
</span>
</div>
</article>
);
};
const TodoList = ({ list = [] }) => {
return (
<main>
{list.map(({ title, completed, id }) => (
<TodoItem key={id} title={title} completed={completed} />
))}
</main>
);
};
const FilterItem = ({ checked, name, onChange }) => {
return (
<label>
{name}
<input
checked={checked}
type="radio"
name={name}
value={name}
onChange={onChange}
/>
</label>
);
};
const FiltersPanel = ({ curFilter, updateFilter }) => {
const onFiltersChange = (event) => {
const newFilter = event.target.value;
updateFilter(newFilter);
};
return (
<form className={"filters-panel"}>
<h2>Select filter</h2>
<FilterItem
checked={curFilter === COMPLETE_FILTER.All}
name={COMPLETE_FILTER.All}
onChange={onFiltersChange}
/>
<FilterItem
checked={curFilter === COMPLETE_FILTER.Completed}
name={COMPLETE_FILTER.Completed}
onChange={onFiltersChange}
/>
<FilterItem
checked={curFilter === COMPLETE_FILTER.Incompleted}
name={COMPLETE_FILTER.Incompleted}
onChange={onFiltersChange}
/>
</form>
);
};
function App() {
const [filter, setFilter] = useState(COMPLETE_FILTER.All);
/**
* Функция-утилита, содержащая в себе логику по фильтрации данных для отображения
* @param list - массив данных, которые мы будем фильтровать
* @param curFilter - текущее знач-е фильтра
*/
const getfilteredTodos = (list, curFilter = COMPLETE_FILTER.All) => {
if (curFilter === COMPLETE_FILTER.All) return list;
const needCompleted = curFilter === COMPLETE_FILTER.Completed;
return list.filter((item) => item.completed === needCompleted);
};
/**
* Здесь мы сохраняем уже отфильтрованные данные
* Я использовал переменную с useMemo, т.к. в моем случае данные статичны
* Если вы будете загружать данные с сервера, то просто используйте вместо этого варианта - состояние (для функц. компонетн useState)
*/
const filteredTodos = useMemo(() => getfilteredTodos(data, filter), [filter]);
/**
* Обработчик смены текущего фильтра
*/
const handleFilterUpdate = useCallback((newFilter) => {
setFilter(newFilter);
}, []);
return (
<div className="App">
<FiltersPanel curFilter={filter} updateFilter={handleFilterUpdate} />
<TodoList list={filteredTodos} />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
.App {
font-family: sans-serif;
text-align: center;
display: flex;
flex-direction: column;
gap: 10px;
}
body {
background: #e4e4e4;
}
main {
padding: 15px;
border: 2px dashed lightgray;
background: #fff;
display: flex;
flex-direction: column;
gap: 10px;
}
.todoItem {
border: 1px solid lightgray;
}
.completed {
color: lightgreen;
}
.incompleted {
color: lightpink;
}
.filters-panel {
border: 2px dashed lightgray;
background: #fff;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Если вам захочется посмотреть на решение изнутри, то вот ссылка на sandbox