I have this app which currently fetches some posts from an API using Thunk. I need to filter these by the criteria stored in the state in my searchBar component when the search button is pressed, using Reselect. I know I have to adapt my reducer to “accept” the local state of my SearchBar component, but I´m a bit lost on how to set it up and on how to use ReSelect. Could anyone help?
app component
class ItemList extends Component {
componentDidMount() {
this.props.fetchData('https://football-players-b31f2.firebaseio.com/players.json');
}
render() {
if (this.props.hasErrored) {
return <p>Sorry! There was an error loading the items</p>;
}
if (this.props.isLoading) {
return <p>Loading…</p>;
}
return (
<SearchBar items={this.props.items}/>
);
}
}
const mapStateToProps = (state) => {
return {
items: state.items,
hasErrored: state.itemsHasErrored,
isLoading: state.itemsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: (url) => dispatch(itemsFetchData(url))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ItemList);
SearchBar component
class SearchBar extends Component {
constructor () {
super();
this.state = {
age: '',
name: '',
selected: "position"
};
this.handleChange = this.handleChange.bind(this);
this.handleSelectChange=this.handleSelectChange.bind(this)
}
handleChange (evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
handleSelectChange (evt) {
this.setState({selected: event.target.value});
}
render () {
const unique = [...new Set(props.items.map(item => item.position))];
const items = unique.map((i, index) =>{return ( <option value={i} key={index}> {i}</option> )
});
return (
<div>
<input type="text" name="name"
pattern= "/[a-zA-Z]+" onChange={this.handleChange} />
<input type="number" name="age"
min="18" max="40" onChange={this.handleChange} />
<React.Fragment>
<select onChange={this.handleSelectChange} value={this.state.selected}>{items}</select>
</React.Fragment>
<button>Search</button>
</div>
);
}
}
Reducer
export default combineReducers({
items,
itemsHasErrored,
itemsIsLoading
});
export function itemsHasErrored(state = false, action) {
switch (action.type) {
case 'ITEMS_HAS_ERRORED':
return action.hasErrored;
default:
return state;
}
}
export function itemsIsLoading(state = false, action) {
switch (action.type) {
case 'ITEMS_IS_LOADING':
return action.isLoading;
default:
return state;
}
}
export function items(state = [], action) {
switch (action.type) {
case 'ITEMS_FETCH_DATA_SUCCESS':
return action.items;
default:
return state;
}
}
Actions
export function itemsHasErrored(bool) {
return {
type: 'ITEMS_HAS_ERRORED',
hasErrored: bool
};
}
export function itemsIsLoading(bool) {
return {
type: 'ITEMS_IS_LOADING',
isLoading: bool
};
}
export function itemsFetchDataSuccess(items) {
return {
type: 'ITEMS_FETCH_DATA_SUCCESS',
items
};
}
export function itemsFetchData(url) {
return (dispatch) => {
dispatch(itemsIsLoading(true));
fetch(url)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(itemsIsLoading(false));
return response;
})
.then((response) => response.json())
.then((items) => dispatch(itemsFetchDataSuccess(items)))
.catch(() => dispatch(itemsHasErrored(true)));
};
}