Reputation: 673
I am trying to implement a search functionality with redux in my react application. But somehow there is something wrong and I can't find the issue.
// Header.js
import React from 'react';
import { useDispatch } from 'react-redux';
import { SEARCH_PIZZA } from '@ActionTypes';
...other imports
// styles for Header
const useStyles = makeStyles(...)
function Header() {
const classes = useStyles();
const dispatch = useDispatch();
const [searchValue, setSearchValue] = React.useState('');
const handleSearch = (e) => {
setSearchValue(e.target.value);
dispatch({ type: SEARCH_PIZZA, searchValue });
};
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search…"
inputProps={{ 'aria-label': 'search' }}
name="pizzaSearch"
value={searchValue}
onChange={handleSearch}
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
/>
</div>
</Toolbar>
</AppBar>
</div>
);
}
export default Header;
// reducer.js
import { SEARCH_PIZZA } from '@ActionTypes';
const initialState = {
...other state values
searchValue: '',
};
function shopReducer(state = initialState, action) {
switch (action.type) {
case SEARCH_PIZZA:
const { searchValue } = action;
const pizzaList = state.pizzaList.filter((item) =>
item.name.match(searchValue, 'gmi'),
);
return { ...state, searchValue, pizzaList };
...other code
}
}
export default shopReducer;
to mention I have a persisted state setup
Upvotes: 1
Views: 231
Reputation: 39250
You should store the search in state or search component and use a selector to get the filtered pizza list like the example below:
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;
const initialState = {
pizzas: [
{ id: 1, name: 'pizza 1' },
{ id: 2, name: 'pizza 2' },
],
searchValue: '',
};
//action types
const SET_SEARCH_VALUE = 'SET_SEARCH_VALUE';
//action creators
const setSearchValue = (searchValue) => ({
type: SET_SEARCH_VALUE,
payload: searchValue,
});
function reducer(state = initialState, { type, payload }) {
if (type === SET_SEARCH_VALUE) {
return { ...state, searchValue: payload };
}
return state;
}
//selectors
const selectPizzas = (state) => state.pizzas;
const selectSearch = (state) => state.searchValue;
const selectFilteredPizzas = createSelector(
[selectPizzas, selectSearch],
(pizzas, searchValue) =>
pizzas.filter((pizza) =>
pizza.name.match(searchValue, 'gmi')
)
);
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(() => (next) => (action) =>
next(action)
)
)
);
const Filter = React.memo(function Filter() {
const dispatch = useDispatch();
const searchValue = useSelector(selectSearch);
return (
<label>
search
<input
type="text"
value={searchValue}
onChange={(e) =>
dispatch(setSearchValue(e.target.value))
}
/>
</label>
);
});
const Pizza = React.memo(function Pizza({ pizza }) {
return <li>{pizza.name}</li>;
});
const Pizzas = React.memo(function Pizzas() {
const pizzas = useSelector(selectFilteredPizzas);
return (
<ul>
{pizzas.map((pizza) => (
<Pizza key={pizza.id} pizza={pizza} />
))}
</ul>
);
});
const App = () => {
return (
<div>
<Filter />
<Pizzas />
</div>
);
};
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<div id="root"></div>
More information on selectors can be found here
Upvotes: 1