admin管理员组

文章数量:1315788

I have buttons Apply Filters and Reset Filters. When I click Apply Filters, it applies filters in the UI immediately, but when I click Reset Filters, it only is clicked after being clicked two times. Here's the code:

import { useState, useEffect } from 'react';
import { getBusinesses } from '../api/DBRequests';
import categories from '../constants/categories';

const BusinessListPage = () => {
  const [businesses, setBusinesses] = useState([]);
  const [filters, setFilters] = useState({
    category: '',
    state: '',
    minPrice: '',
    maxPrice: '',
    minRevenue: '',
    maxRevenue: '',
  });
  const [sortBy, setSortBy] = useState('');
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    fetchBusinesses();
  }, []);

  const fetchBusinesses = async () => {
    setLoading(true);
    try {
      const adjustedSortBy =
        sortBy === 'asc' ? 'asc' : sortBy === 'desc' ? 'desc' : '';
      const businesses = await getBusinesses(adjustedSortBy, filters);
      setBusinesses(businesses);
    } catch (error) {
      console.error('Error fetching businesses:', error.message);
    } finally {
      setLoading(false);
    }
  };

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFilters((prevFilters) => ({ ...prevFilters, [name]: value }));
  };

  const handleResetFilters = () => {
    setFilters({
      category: '',
      state: '',
      minPrice: '',
      maxPrice: '',
      minRevenue: '',
      maxRevenue: '',
    });
    setSortBy('');
    fetchBusinesses();
  };

  return (
    <div className="container-fluid mt-4">
      <div className="row">
        <div className="col-md-3">
          <div className="bg-light border p-3">
            <h5>Filters</h5>
            <div className="mb-3">
              <label htmlFor="category" className="form-label">
                Category
              </label>
              <select
                id="category"
                name="category"
                className="form-control"
                value={filters.category}
                onChange={handleInputChange}
              >
                <option value="">All Categories</option>
                {categories.map((category, index) => (
                  <option key={index} value={category}>
                    {category}
                  </option>
                ))}
              </select>
            </div>
            <button
              className="btn btn-primary btn-block"
              onClick={fetchBusinesses}
            >
              Apply Filters
            </button>
            <button
              className="btn btn-secondary btn-block mt-2"
              onClick={handleResetFilters}
            >
              Reset Filters
            </button>
          </div>
        </div>

        {/* Businesses List Section */}
        <div className="col-md-9">
          {loading && <p className="d-block mx-auto">Loading</p>}
          <div className="row">
            {businesses.length === 0 && !loading && (
              <div className="col-12">
                <p className="mt-4 text-center">No businesses found.</p>
              </div>
            )}
            {businesses.map((business) => (
              <div className="col-md-4 mb-4" key={business._id}>
                <div className="card h-100">
                  <div className="card-body">
                    <h5 className="card-title">{business.name}</h5>
                  </div>
                  <div className="card-footer">
                    <a
                      href={`/business/${business._id}`}
                      className="btn btn-primary btn-sm btn-block"
                    >
                      View Details
                    </a>
                  </div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default BusinessListPage;

I have buttons Apply Filters and Reset Filters. When I click Apply Filters, it applies filters in the UI immediately, but when I click Reset Filters, it only is clicked after being clicked two times. Here's the code:

import { useState, useEffect } from 'react';
import { getBusinesses } from '../api/DBRequests';
import categories from '../constants/categories';

const BusinessListPage = () => {
  const [businesses, setBusinesses] = useState([]);
  const [filters, setFilters] = useState({
    category: '',
    state: '',
    minPrice: '',
    maxPrice: '',
    minRevenue: '',
    maxRevenue: '',
  });
  const [sortBy, setSortBy] = useState('');
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    fetchBusinesses();
  }, []);

  const fetchBusinesses = async () => {
    setLoading(true);
    try {
      const adjustedSortBy =
        sortBy === 'asc' ? 'asc' : sortBy === 'desc' ? 'desc' : '';
      const businesses = await getBusinesses(adjustedSortBy, filters);
      setBusinesses(businesses);
    } catch (error) {
      console.error('Error fetching businesses:', error.message);
    } finally {
      setLoading(false);
    }
  };

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFilters((prevFilters) => ({ ...prevFilters, [name]: value }));
  };

  const handleResetFilters = () => {
    setFilters({
      category: '',
      state: '',
      minPrice: '',
      maxPrice: '',
      minRevenue: '',
      maxRevenue: '',
    });
    setSortBy('');
    fetchBusinesses();
  };

  return (
    <div className="container-fluid mt-4">
      <div className="row">
        <div className="col-md-3">
          <div className="bg-light border p-3">
            <h5>Filters</h5>
            <div className="mb-3">
              <label htmlFor="category" className="form-label">
                Category
              </label>
              <select
                id="category"
                name="category"
                className="form-control"
                value={filters.category}
                onChange={handleInputChange}
              >
                <option value="">All Categories</option>
                {categories.map((category, index) => (
                  <option key={index} value={category}>
                    {category}
                  </option>
                ))}
              </select>
            </div>
            <button
              className="btn btn-primary btn-block"
              onClick={fetchBusinesses}
            >
              Apply Filters
            </button>
            <button
              className="btn btn-secondary btn-block mt-2"
              onClick={handleResetFilters}
            >
              Reset Filters
            </button>
          </div>
        </div>

        {/* Businesses List Section */}
        <div className="col-md-9">
          {loading && <p className="d-block mx-auto">Loading</p>}
          <div className="row">
            {businesses.length === 0 && !loading && (
              <div className="col-12">
                <p className="mt-4 text-center">No businesses found.</p>
              </div>
            )}
            {businesses.map((business) => (
              <div className="col-md-4 mb-4" key={business._id}>
                <div className="card h-100">
                  <div className="card-body">
                    <h5 className="card-title">{business.name}</h5>
                  </div>
                  <div className="card-footer">
                    <a
                      href={`/business/${business._id}`}
                      className="btn btn-primary btn-sm btn-block"
                    >
                      View Details
                    </a>
                  </div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default BusinessListPage;
Share Improve this question edited Jan 30 at 3:25 in43sh asked Jan 30 at 3:20 in43shin43sh 9432 gold badges14 silver badges37 bronze badges 8
  • 1 Calling setFilters() is asynchronous. Setting it to empty values and immediately calling fetchBusinesses() doesn't give the filters state time to update – Phil Commented Jan 30 at 3:37
  • @Phil so what do I do? – in43sh Commented Jan 30 at 3:41
  • 1 I'd change fetchBusinesses() to accept the filters and sortBy as parameters instead of relying on state. Then you can directly control the values; create an "empty filters" object then pass it to both fetchBusinesses() and setFilters() – Phil Commented Jan 30 at 3:47
  • 1 Don't do that please. If the suggestions here don't answer this question, then edit it to provide details. Note also that I revised my 2nd comment (in case you missed that) – Phil Commented Jan 30 at 3:53
  • 1 Fantastic. Another option would be to have two filter state variables; one for editing and another applied state. Then you can use the latter in an effect hook, eg useEffect(fetchBusinesses, [appliedFilters]) – Phil Commented Jan 30 at 4:10
 |  Show 3 more comments

2 Answers 2

Reset to default 2

Calling setFilters() is asynchronous. Setting it to empty values and immediately calling fetchBusinesses() doesn't give the filters state time to update.

I'd change fetchBusinesses() to accept the filters and sortBy as parameters instead of relying on state. Then you can directly control the values; create an "empty filters" object then pass it to both fetchBusinesses() and setFilters()

const initFilters = {
  category: '',
  state: '',
  minPrice: '',
  maxPrice: '',
  minRevenue: '',
  maxRevenue: '',
};
const initSortBy = '';

const BusinessListPage = () => {
  const [filters, setFilters] = useState(initFilters);
  const [sortBy, setSortBy] = useState(initSortBy);
  // etc ...

  // added params with default values coming from state
  const fetchBusinesses = async (f = filters, s = sortBy) => {
    setLoading(true);
    try {
      const adjustedSortBy =
        s === 'asc' ? 'asc' : s === 'desc' ? 'desc' : '';
      setBusinesses(await getBusinesses(adjustedSortBy, f));
    } catch (error) {
      console.error('Error fetching businesses:', error.message);
    } finally {
      setLoading(false);
    }
  };

  const handleResetFilters = () => {
    setFilters(initFilters);
    setSortBy('');
    fetchBusinesses(initFilters, ''); // pass in init values
  };

https://react.dev/reference/react/StrictMode

React strict mode renders your object twice to catch potential bugs early on. This is why you need to "Reset Filters" twice to see it clear up.

Additionally, in useState, if you use strict mode "React will call your initializer function twice in order to help you find accidental impurities." - https://react.dev/reference/react/useState

Another possibility is that your function all "Reset filters" encounters an asynchronous process and waits for it to finish before setting the business.

const businesses = await getBusinesses(adjustedSortBy, filters);

本文标签: javascriptReset Filters button only updates UI after second clickStack Overflow