Dmitriy Shin
Dmitriy Shin

Reputation: 673

React redux search is not working properly

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;

As a result the search returns nothing but an empty array...

to mention I have a persisted state setup

Upvotes: 1

Views: 231

Answers (1)

HMR
HMR

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

Related Questions