Reputation: 1183
I'm trying to filter data based on a simple user search input.
I'm not sure if its the way i'm filtering the data, but whenever I input something in the text box, the data disappears. I can see in dev tools that the state of the query is being stored.
Here's my code in my context file. I'm planning on adding additional filters once the search function is fixed, thus the reason for the more complicated code.
import * as React from "react";
const DefaultState = {
cardListings: [],
filter: {}
};
const CardListingsContext = React.createContext(DefaultState);
export const CardListingsConsumer = CardListingsContext.Consumer;
export class CardListingsProvider extends React.Component {
static applyFilter(cards, filter) {
const { query } = filter;
let result = cards;
if (query) {
const search = query.toLowerCase();
result = result.filter(item => item.title.indexOf(search) !== -1);
}
return result;
}
state = DefaultState;
componentDidMount() {
fetch("http://localhost:9000/mwBase")
.then(res => res.json())
.then(res => {
this.setState({ cardListings: res });
});
}
updateFilter = filter => {
this.setState({
filter
});
};
render() {
const { children } = this.props;
const { cardListings, filter } = this.state;
const filteredListings = CardListingsProvider.applyFilter(
cardListings,
filter
);
return (
<CardListingsContext.Provider
value={{
allListings: cardListings,
cardListings: filteredListings,
updateFilter: this.updateFilter
}}
>
{children}
</CardListingsContext.Provider>
);
}
}
Here's my input form
<form
className={formClasses}
noValidate
onChange={() =>
setTimeout(() => this.props.updateFilter(this.state), 0)
}
>
<p className="mb-1">Refine your results</p>
<div className="form-group">
<input
type="text"
className="form-control form-control-lg"
placeholder="Search for a card..."
name="query"
value={this.state.query}
onChange={event => this.setState({ query: event.target.value })}
/>
</div>
and where the Filter is being applied on my home page:
<CardListingsProvider>
<CardListingsConsumer>
{function(value) {
const { cardListings, updateFilter } = value;
return (
<>
<Filter updateFilter={updateFilter} />
<div className="columns">
{cardListings.map(item => (
<Card key={item.itemId} card={item} />
))}
</div>
</>
);
}}
</CardListingsConsumer>
</CardListingsProvider>
</div>
Here's example of my dataset:
[
{
itemId: [
"120901386991"
],
title: [
"1952 Topps Mickey Mantle Chase Card Box 18 packs 5 1950s or 1960's cards per box"
],
globalId: [
"EBAY-US"
],
subtitle: [
"3 BX LOT. 1 VINTAGE PK PER 25 BOXES* LOOK 4 1952 MANTLE"
],
primaryCategory: [
{
categoryId: [
"213"
],
categoryName: [
"Baseball Cards"
]
}
],
secondaryCategory: [
{
categoryId: [
"156521"
],
categoryName: [
"Vintage Non-Sport Cards"
]
}
],
galleryURL: [
"https://thumbs4.ebaystatic.com/m/m1mtMB65mAApWQ2EhJy4qWA/140.jpg"
],
viewItemURL: [
"https://rover.ebay.com/rover/1/711-53200-19255-0/1?ff3=2&toolid=10044&campid=5338164673&customid=watchbask&lgeo=1&vectorid=229466&item=120901386991"
],
paymentMethod: [
"PayPal"
],
autoPay: [
"true"
],
location: [
"USA"
],
country: [
"US"
],
shippingInfo: [
{
shippingServiceCost: [
{
@currencyId: "USD",
__value__: "0.0"
}
],
shippingType: [
"Free"
],
shipToLocations: [
"Worldwide"
],
expeditedShipping: [
"false"
],
oneDayShippingAvailable: [
"false"
],
handlingTime: [
"1"
]
}
],
sellingStatus: [
{
currentPrice: [
{
@currencyId: "USD",
__value__: "118.0"
}
],
convertedCurrentPrice: [
{
@currencyId: "USD",
__value__: "118.0"
}
],
sellingState: [
"Active"
],
timeLeft: [
"P10DT14H59M31S"
]
}
],
listingInfo: [
{
bestOfferEnabled: [
"false"
],
buyItNowAvailable: [
"false"
],
startTime: [
"2012-04-23T16:52:17.000Z"
],
endTime: [
"2019-10-23T16:52:17.000Z"
],
listingType: [
"FixedPrice"
],
gift: [
"false"
],
watchCount: [
"443"
]
}
],
returnsAccepted: [
"false"
],
condition: [
{
conditionId: [
"1000"
],
conditionDisplayName: [
"Brand New"
]
}
],
isMultiVariationListing: [
"false"
],
pictureURLLarge: [
"https://i.ebayimg.com/00/s/NTAwWDMxNA==/z/sT8AAOSw62VZv9qQ/$_1.JPG"
],
topRatedListing: [
"false"
]
},
Upvotes: 1
Views: 1964
Reputation: 3679
In your case title is an array of string. If it is supposed to contain only one element. You can change your filter function from
result.filter(item => item.title.indexOf(search) !== -1);
to
result.filter(item => item.title[0].indexOf(search) !== -1);
If the title array contains multiple items, You could do use Array.some
result.filter(item =>
item.title.some(eachTitle => {
return eachTitle.indexOf(search) !== -1
})
)
And if you need case insensitive filter, you might need to change the filter function on that aspect too.
const search = query.toLowerCase();
result.filter(item => item.title[0].toLowerCase().indexOf(search) !== -1);
Looks like the code snippet you have posted might not be complete. I see some unbalanced parentheses for applyFilter
Function in your Provider component.
static applyFilter(cards, filter) {
const { query } = filter;
let result = cards;
if (query) {
const search = query.toLowerCase();
result = result.filter(item => item.title.indexOf(search) !== -1);
}
state = DefaultState;
Also I'm wondering why would you need a setTimeout
to call setState
function in Filter
component. The below
onChange={() =>
setTimeout(() => this.props.updateFilter(this.state), 0)
}
You can get rid of that as well.
I have made some edits to complete applyFilter
function to return the filtered data. Please have a look at the below code and Run Code Snippet
to see the code in action. Hope this helps!
// Provider Class
const DefaultState = {
cardListings: [],
filter: {}
};
const CardListingsContext = React.createContext(DefaultState);
const CardListingsConsumer = CardListingsContext.Consumer;
class CardListingsProvider extends React.Component {
static applyFilter(cards, filter) {
const {
query
} = filter;
let result = cards;
if (query) {
const search = query.toLowerCase();
result = result.filter(item => item.title[0].toLowerCase().indexOf(search) !== -1);
}
return result;
}
state = DefaultState;
componentDidMount() {
Promise.resolve([
{
itemId: ['1'],
title: ['Apple']
},
{
itemId: ['2'],
title: ['Orange']
},
{
itemId: ['3'],
title: ['Peach']
}
]).then(res => {
this.setState({
cardListings: res
});
});
}
updateFilter = filter => {
this.setState({
filter
});
};
render() {
const {
children
} = this.props;
const {
cardListings,
filter
} = this.state;
const filteredListings = CardListingsProvider.applyFilter(
cardListings,
filter
);
return ( <
CardListingsContext.Provider value = {
{
allListings: cardListings,
cardListings: filteredListings,
updateFilter: this.updateFilter
}
} >
{
children
}
</CardListingsContext.Provider>
);
}
}
class Filter extends React.Component {
state = { query: "" };
render() {
return (
<form
noValidate
onChange={() =>
setTimeout(() => this.props.updateFilter(this.state), 0)
}
>
<p className="mb-1">Refine your results</p>
<div className="form-group">
<input
type="text"
className="form-control form-control-lg"
placeholder="Search for a card..."
name="query"
value={this.state.query}
onChange={event => this.setState({ query: event.target.value })}
/>
</div>
</form>
);
}
}
class Home extends React.Component {
render() {
return (
<div>
<CardListingsProvider>
<CardListingsConsumer>
{function(value) {
const { cardListings, updateFilter } = value;
return (
<React.Fragment>
<Filter updateFilter={updateFilter} />
<div className="columns">
{cardListings.map(item => (
<div key={item.itemId}>{JSON.stringify(item)}</div>
))}
</div>
</React.Fragment>
);
}}
</CardListingsConsumer>
</CardListingsProvider>
</div>
);
}
}
ReactDOM.render( <Home /> , document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 1