问题描述
构建可重复使用的,可链接的过滤器组件的最反应"方式是什么?
What's the most "React"y way to build reusable, chainable filter components?
假设我有一个输入数组:
Let's say I have an input array:
[
{name: 'Generic t-shirt', size: 'xxl', available: 35},
{name: 'Generic t-shirt', size: 'md', available: 2},
{name: 'Generic t-shirt', size: 'sm', available: 5},
{name: 'Really awesome shirt', size: 'md', available: 0}
]
然后使用关键字搜索名称,下拉列表中的大小以及已售完"布尔值复选框以了解可用性.
And a keyword search for the name, a dropdown for size, and a "sold out" boolean checkbox for availability.
现在,我在渲染循环中包含了过滤代码:
Right now, I have the filtering code inside the rendering loop:
const [productsFilteredByFullText, setProductsFilteredByFullText] = useState;
const [productsFilteredBySize, setProductsFilteredBySize] = useState;
const [productsFilteredByAvailability, setProductsFilteredByAvailability] = useState;
const searchResults = useMemo(() => {
let filteredProducts = eventsFromAPI; // array of products as input
filteredProducts = filteredEvents.filter(product => fullTextFilter(product));
filteredProducts = filteredEvents.filter(product => sizeFilter(product));
filteredProducts = filteredEvents.filter(product => availabilityFilter(product));
return filteredProducts;
}, [productsFilteredByFullText, productsFilteredBySize, productsFilteredByAvailability]);
以及JSX中的UI:
<div>
// Fulltext search
<input
type="text"
placeholder="Keyword search"
value={searchTerm}
onChange={fullTextHandler}
/>
// Size dropdown
<Dropdown
options={allAvailableSizes}
onChange={sizeHandler}
/>
// Sold out checkbox
<input
name="soldout"
type="checkbox"
checked={productsFilteredByAvailability}
onChange={availabilityHandler}
/>
<h1>Results</h1>
{filteredProducts.map(item => (
<Product item={item} />
))}
</div>
这有效,但是根本不是非常可重用的.假设我还有另一种产品围巾,它们都是一种尺寸,但是我仍然希望能够重复使用过滤器组件和逻辑来命名和使用.
This works, but is not very reusable at all. Let's say I have another product category, scarves, that are all one size, but I still want to be able to re-use the filter component and logic for name and availability.
是否有一种方法可以将过滤器逻辑和演示JSX模块化/组件化为单独的过滤器组件,并能够将它们任意地链接在一起?像这样的伪代码:
Is there a way to modularize/componentize BOTH the filter logic AND the presentation JSX into separate filter components, and be able to chain them together arbitrarily? Something like this pseuocode:
<TShirtList>
<NameFilter/>
<SizeFilter/>
<AvailabilityFilter/>
</TShirtList>
<ScarfList>
<NameFilter/>
<AvailabilityFilter/>
</ScarfList>
<ServicesList>
<NameFilter/>
</ServicesList>
每个过滤器都是自己的组件,可以插入任何地方的任何产品系列中吗?就像React组件如何提供其他组件可以使用的自己的逻辑/功能一样(输入产品数组,过滤的产品数组+ JSX UI输出,但可链接).
So that each filter is its own component, able to be inserted into any array of products anywhere? Like how can a React component also provide its own logic/functions that other components can use (product array in, filtered product array + JSX UI out, but chainable).
这是思考这个问题的正确方法吗?我对如何最好地在体系结构上构建感到困惑.
Is that even the right way to think about this problem...? I'm confused about how to best build this architecturally.
推荐答案
该问题的答案
我建议单独显示&数据逻辑通过使用钩子.将所有与数据相关的逻辑移到一个钩子中,然后将该钩子导入到react组件中.
I suggest separate display & data logic by using hooks. Move all data related logic into a hook, and import that hook to a react component.
// useProducts.js
const useProducts = () => {
const [products, setProducts = useState([]);
const [filters, setFilters] = useState({}); // eg. filters = { size: 'large', availability: 'instock' }
// Get all products on initial load
useEffect(() => {
fetch(....).then(response => setProducts(response.data));
}, []);
// function to set filter.
const setFilter = (name, value) => {
setFilters({ ...filters, [name]: value });
}
// Loop filters and filter product
const filteredProducts = [...products];
for (const [key, value] of Object.entries(filters)) {
// Get list of products from somewhere.
filteredProducts = filterProducts.filter(p => p[key] === value);
}
return [filteredProducts, { setFilter }];
}
// Product List
import React from 'react';
import useProducts from './useProducts';
const ProductList = (props) => {
const [products, { setFilter }] = useProducts();
return (
<div>
<div className="toolbar">
<NameFilter onChange={value => setFilter('name', value)} />
<SizeFilter onChange={value => setFilter('size', value)} />
<AvailabilityFilter onChange={value => setFilter('availability', value)} />
</div>
<div>
{products.map(p => <ProductItem {...p} />}
</div>
</div>
);
}
此外,您甚至可以通过在过滤器组件本身中导入{setFilter}对其进行更多模块化.您可以从ProductList中删除"onChange".
Additionally, you can even modularize it more by import { setFilter } in the filter component itself. And you can remove the 'onChange' from the ProductList.
const NameFilter = () => {
const [, { setFilter }] = useProducts();
return (
<input
type="text"
onChange={e => setFilter('name', e.target.value)}
/>
);
}
// And now, we can remove onChange from each filter in Product List component
<div>
<div className="toolbar">
<NameFilter />
<SizeFilter />
<AvailabilityFilter />
</div>
<div>
{products.map(p => <ProductItem {...p} />}
</div>
</div>
*注意:上面的代码仅是psuedo,它只是说明了想法.
*Note: Above code is just psuedo, it just shows the idea.
这篇关于在React中构建可链接的过滤器组件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!