React Navigation Filter only works when going back to the home screen

Viewed 43

within my React ecommerce store, I have navigation links that take you to the products filtered by whichever link you selected. There are also filter buttons within the product display. Everything works fine, however, the navigation links only work when I go back to the home page. Example. I can click on mens & it filters to mens items. Then I can go home and from there click on womens and it filters to womens items. However, if I click on mens and then click on womens items, I see the path change in the url to womens but the filtered products on mens.

Here is a small sample of the code from the 3 components I believe are leading to this.:

App.js

class App extends React.Component {
  state= {
    cart: []
  }

  updateCart = (cartItem) => {
    this.setState({cart: [...this.state.cart, cartItem]})
  }
  removeFromCart = (id) => {
    this.setState({cart: this.state.cart.filter(i => i.id !== id)})
  }
  render() {
    return (
    <div className="App">

<Router>
      <Navigation/> 
  <Routes>
  <Route path="/" element={<Home />} />
  <Route path="/men" element={ <ButtonMenu updateCart={this.updateCart} path={'men'}/>} />
  <Rou</Routes>
      <FooterNav/>
  </Router>te path="/women" element={ <ButtonMenu updateCart={this.updateCart} path={'women'}/>} /
 </div>
    )
  }
}

export default App;

Navigation.js

class Navigation extends Component{
    
 
  render(){
        return(<>
            
<div>
<nav className="navbar navbar-expand-lg  ">
  <div className="container-fluid">
    <a className="navbar-brand" href="#"><img src = {logo} alt="logo" width = "150rem"/></a>
    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span className="navbar-toggler-icon"></span>
    </button>
    <div className="collapse navbar-collapse" id="navbarSupportedContent">
      <ul className="nav justify-content-center myNav " >
        <li className="nav-item">
          <Link className="nav-link active" aria-current="page" to="/">Home</Link>
        </li>
        <li className="nav-item">
        <Link className="nav-link " aria-current="page" to="/men">Men</Link>
        </li>
        <li className="nav-item">
        <Link className="nav-link " aria-current="page" to="/women">Women</Link>
        </li>
             </ul>
    </div>
  </div>
</nav>
</div>
              </>
        )
    }
}

export default Navigation;


ButtonMenu.js

export const  ProductList =[ 70+ objects with category of men, women...etc ]


const CATEGORIES = ['All', 'Men', 'Women', 'Jersey', "Hats",'Shirts', 'Accessories', 'Collectable', 'Top-Products', 'New-Arrivals'];

 


class ButtonMenu extends Component {
    constructor(props) {
      super(props);
      this.state = {
        products: ProductList
      };

    }
    

    getCategory = (category) => {
      if (category === "all") {
        this.setState({ products: ProductList });
      } else {
        const filter = ProductList.filter((d) => d.category === category);
        if (filter) {
          this.setState({ products: filter });
        }
      }
    };

    componentDidMount(){
   
        const path = this.props.path
        if (path === "all") {
        this.setState({ products: ProductList });
      } else {
        const filter = ProductList.filter((d) => d.category === path);
        if (filter) {
          this.setState({ products: filter });
        }
      }
    }

    render() {
      return (
        <>
          <div className="mainbtn-menu-container">
            
            {CATEGORIES.map((item, index) => (
                 <button
              key={index}
                className="main-btn"
                onClick={() => { this.getCategory(item.toLowerCase()) } }
                value={item.toLowerCase()}
              >
               {item} 
              </button> 
            ))}
            </div>

            <Products products={this.state.products} updateCart={this.props.updateCart} />
         
        </>
      );
    }
  }
  
  export default ButtonMenu;
1 Answers

The ButtonMenu component doesn't handle when props update to update any local state. This would be handled by the componentDidUpdate lifecycle hook. That said, it's considered anti-pattern to duplicate React state or store derived state. You can simply filter the ProductList array inline when rendering based on the path prop.

Example:

class ButtonMenu extends Component {
  state = {
    category: 'all',
  }

  render() {
    return (
      <>
        <div className="mainbtn-menu-container">
          {CATEGORIES.map((item, index) => (
            <button
              key={index}
              className="main-btn"
              onClick={() => this.setState({
                category: item.toLowerCase(),
              })}
              value={item.toLowerCase()}
            >
             {item} 
            </button> 
          ))}
        </div>

        <Products
          products={ProductList
            .filter(el => {
              if (this.state.category === 'all') {
                return true;
              }
              return el.category === this.state.category;
            })
            .filter(el => {
              if (this.props.path === 'all') {
                return true;
              }
              return el.gender === path;
            })
          }
          updateCart={this.props.updateCart}
        />
      </>
    );
  }
}

I suggest converting ButtonMenu to a function component and rendering a single route with a route param that can be read by the component.

Example:

<Router>
  <Navigation/> 
  <Routes>
    <Route path="/" element={<Home />} />
    <Route
      path="/:gender"
      element={<ButtonMenu updateCart={this.updateCart} />}
    />
  </Routes>
  <FooterNav/>
</Router>

...

import { useParams } from 'react-router-dom';

const ButtonMenu = ({ updateCart }) => {
  const { gender } = useParams();
  const [category, setCategory] = React.useState('all');

  return (
    <>
      <div className="mainbtn-menu-container">
        {CATEGORIES.map((item, index) => (
          <button
            key={index}
            className="main-btn"
            onClick={() => this.setCategory(item.toLowerCase())}
            value={item.toLowerCase()}
          >
           {item} 
          </button> 
        ))}
      </div>

      <Products
        products={ProductList
          .filter(el => {
            if (category === 'all') {
              return true;
            }
            return el.category === category;
          })
          .filter(el => {
            if (gender === 'all') {
              return true;
            }
            return el.gender === gender;
          })
        }
        updateCart={updateCart}
      />
    </>
  );
}
Related