简体   繁体   中英

How to conditionally reset state count with react hooks

After the user submits my form, the handleSubmit function runs and counts the number of li elements with class names containing "show" in my div showing all search results. It adds these counts all up with setStoreCount(prevCount => prevCount + 1) and I update my FAQ {searchCount} UI showing the number of search results with

useEffect(() => {
  setSearchCount(storeCount) // collect all storeCount
}, [storeCount])

The problem: Say that the first search gets 5 results.

Then the UI would show FAQ 5 . If you search the same thing again, you still get 5 results and would expect the UI to still show FAQ 5 but because the storeCount cannot reset to 0 it shows FAQ 10 (5 + 5) and then FAQ 15 (5 + 5 + 5) and so on.

Because my useEffect conditionally runs whenever storeCount changes, I cannot reset it to 0 because then I would always show FAQ 0 .

How do I show the number of search results while refreshing the count on every search in the code below?

import React, { useState, useRef, useEffect } from 'react'

import ContentsWrap from '../../components/contents-wrap'
import FrequentList from './tabs/frequent'
import EstimateList from './tabs/estimate'
import ReturnList from './tabs/return'
import EtcList from './tabs/etc'
import SubsidiaryList from './tabs/subsidiary'
import ServiceList from './tabs/service'
import InList from './tabs/in'
import OutList from './tabs/out'

import styles from './index.module.scss'

export default function FAQ () {
  const [tab, setTab] = useState('frequent')
  const [storeText, setStoreText] = useState('') // stores searchbar text as user types
  const [searchText, setSearchText] = useState('') // sends searchbar text to List component props on form submit
  const [storeCount, setStoreCount] = useState(0) // stores count of each li element className that includes 'show'
  const [searchCount, setSearchCount] = useState(0) // shows search count in UI
  const searchContainer = useRef(null)
  const handleSubmit = e => {
    e.preventDefault()
    setSearchText(storeText)
    setTab('all')
    setTimeout(() => {
      // gets all <ul> children of <div class = "faq-list-wrap">
      for (let i = 0; i < searchContainer.current.children.length; i++) {
      // gets all <li> children of each <ul>
        for (let j = 0; j < searchContainer.current.children[i].children.length; j++) {
        // checks if each li class name has 'show'
          if (searchContainer.current.children[i].children[j].className.includes('show')) {
            console.log('count added')
            setStoreCount(prevCount => prevCount + 1)
          }
        }
      }
    }, 100) // setTimeOut needed to target searchContainer after search and not before
  }
  const handleChange = newId => {
    setTab(newId)
    setStoreText('') // clear input value on tab click
    setSearchText('') // show all search items on tab click
  }
  useEffect(() => {
    setSearchCount(storeCount) // collect all storeCount
  }, [storeCount])
  return (
    <ContentsWrap>
      <div className="content-wrap content-width">
        {/* <!-- S: faq-wrap --> */}
        <div className="faq-wrap">
          <div className="faq-title-wrap"><h2 className="title">FAQ {searchCount}</h2></div>
          <form onSubmit={handleSubmit}>
            <input
              type="text"
              placeholder="Search"
              className={styles.searchBox}
              value={storeText}
              onChange={e => {
                setStoreText(e.target.value)
              }}
            />
          </form>

          {/* <!-- S: faq-list-wrap --> */}
          <div className="faq-list-wrap" ref={searchContainer} >
            {tab === 'all' && (
              <>
                <FrequentList searchText={searchText} />
                <EstimateList searchText={searchText} />
                <ReturnList searchText={searchText} />
                <EtcList searchText={searchText} />
                <SubsidiaryList searchText={searchText} />
                <ServiceList searchText={searchText} />
                <InList searchText={searchText} />
                <OutList searchText={searchText} />
              </>
            )}
            {tab === 'frequent' && (
              <FrequentList searchText={searchText}/>
            )}
            {tab === 'estimate' && (
              <EstimateList searchText={searchText}/>
            )}
            {tab === 'return' && (
              <ReturnList searchText={searchText} />
            )}
            {tab === 'subsidiary' && (
              <SubsidiaryList searchText={searchText} />
            )}
            {tab === 'service' && (
              <ServiceList searchText={searchText} />
            )}
            {tab === 'in' && (
              <InList searchText={searchText} />
            )}
            {tab === 'out' && (
              <OutList searchText={searchText} />
            )}
            {tab === 'etc' && (
              <EtcList searchText={searchText} />
            )}
          </div>
          {/* <!-- E: faq-list-wrap --> */}
        </div>
        {/* <!-- E: faq-wrap --> */}
      </div>
    </ContentsWrap>
  )
}

The problem here is in your setStoreCount logic.

In this line in your for loop:

if (searchContainer.current.children[i].children[j].className.includes('show')) {
    console.log('count added')
    setStoreCount(prevCount => prevCount + 1)
}

It adds the 1 to the prevCount meaning if the prevCount is 5 (which is the number of results on the first search, it adds 1 to that and so on.

What you can do is reset the storeCount before you add the number of elements found. like so:

setTimeout(() => {
      setStoreCount(0)
      // gets all <ul> children of <div class = "faq-list-wrap">
      for (let i = 0; i < searchContainer.current.children.length; i++) {
      // gets all <li> children of each <ul>
        for (let j = 0; j < searchContainer.current.children[i].children.length; j++) {
        // checks if each li class name has 'show'
          if (searchContainer.current.children[i].children[j].className.includes('show')) {
            console.log('count added')
            setStoreCount(prevCount => prevCount + 1)
          }
        }
      }
    }, 100) // setTimeOut needed to target searchContainer after search and not before

But this might make it like, it show FAQ 0 then the rest of the count after it loops. So I suggest count it first before you set the count, like so.

setTimeout(() => {
      let count = 0
      // gets all <ul> children of <div class = "faq-list-wrap">
      for (let i = 0; i < searchContainer.current.children.length; i++) {
      // gets all <li> children of each <ul>
        for (let j = 0; j < searchContainer.current.children[i].children.length; j++) {
        // checks if each li class name has 'show'
          if (searchContainer.current.children[i].children[j].className.includes('show')) {
            console.log('count added')
            count++
          }
        }
      }
      setStoreCount(count)
    }, 100) // setTimeOut needed to target searchContainer after search and not before

Your loop is just incrementing the storeCount perpetually. You should start a counter at zero at the start of the loop the loop and then assign the final value of the counter into your storeCount using setStoreCount.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM