【问题标题】:React FindIndex is constantly returning -1 even with the item clearly in the arrayReact FindIndex 不断返回-1,即使该项目清楚地在数组中
【发布时间】:2021-07-07 07:04:36
【问题描述】:

我正在为一个电子商务项目开发过滤器。

它有一个活动过滤器部分,其中显示处于活动状态的过滤器,并且可以使其处于非活动状态。此功能有效,正确找到索引并删除正确的对象。我使用了一些相同的代码来创建切换过滤器活动状态的函数,但由于某种原因,即使控制台日志显示数组中的对象,并且显示对象完全相同,它也会不断返回 -1。我很困惑。

这是数组的控制台日志:

[
    {
        "section": "Section 1",
        "option": "Option 1"
    },
    {
        "section": "Filter Section",
        "option": "Option 1"
    },
    {
        "section": "Section 1",
        "option": "Option 2"
    }
]

这是我要查找的对象的控制台日志:

{
    "section": "Filter Section",
    "option": "Option 1"
}

然后控制台为索引记录 -1,而它显然应该是 1。

代码如下:

import styles from '../../styles/SASS/filter.module.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlus, faMinus, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons'
import { useState } from 'react';

export default function Filter(params) {
  // hard coded data for dev purposes
  let myFilters = {sections: [
    {name: "Filter Section", options: ["Option 1", "Option 2", "Option 3"]}
    ]
  };
  
  let [activeFilters, setActiveFilters] = useState([{section: "Section 1", option: "Option 1"},
  {section: "Filter Section", option: "Option 1"},
  {section: "Section 1", option: "Option 2"}
]);

  let [filters, setFilters] = useState(myFilters);
  
  let [openSections, setOpenSections] = useState(["Active Filters"]);

  let isActive = (string) => {
    let section = string.split(":")[0];
    let option = string.split(":")[1];
    let filter = {};
    filter.section = section;
    filter.option = option;
    let currentActiveFilters = [...activeFilters];
    let index = currentActiveFilters.findIndex(activefilter => activefilter == filter);
    // console.log(currentActiveFilters);
    // console.log(filter)
    // console.log(`Index is ${index}`)
    if (index !== -1) {
      return true;
    } else {
      return false;
    }
  }

  let removeActiveFilter = (filter) => {
    let currentActiveFilters = [...activeFilters];
    let index = currentActiveFilters.findIndex((activefilter) => {
      activefilter == filter;
    });
    currentActiveFilters.splice(index, 1);
    setActiveFilters(currentActiveFilters);
  }

  let toggleActive = (string) => {
    let section = string.split(":")[0];
    let option = string.split(":")[1];
    let filter = {};
    filter.section = section;
    filter.option = option;
    let currentActiveFilters = [...activeFilters];
    let index = currentActiveFilters.findIndex((activefilter) => {
      // console.log(activefilter);
      // console.log(filter);
      // activefilter == filter;
      activefilter.section == filter.section && activefilter.option == filter.option
    });
    if (index > 0) {
      currentActiveFilters.splice(index, 1);
      setActiveFilters(currentActiveFilters);
    } else {
      currentActiveFilters.push(filter);
      setActiveFilters(currentActiveFilters);
    }
  }

  let isOpen = (section) => {
    let openedSections = [...openSections];
    let index = openedSections.findIndex(openSection => openSection == section);
    if (index !== -1) {
      return true
    } else {
      return false
    }
  }

  let toggleOpen = (section) => {
    let openedSections = [...openSections];
    let index = openSections.findIndex(openSection => openSection == section);
    if (index === -1) {
      openedSections.push(section);
      setOpenSections(openedSections);
    } else {
      openedSections.splice(index, 1);
      setOpenSections(openedSections);
    }
  }

  return (
    <div className={params.showFilter == true ? `${styles.overlay} ${styles.open}` : `${styles.overlay}`}>
      
      <div className={styles.filter}>

        <div className={styles.header}>
          <p className={styles.title}>Filters</p>
        </div>

        <div className={styles.activeFilters}>
        <div className={styles.sectionHeader} onClick={() => toggleOpen('Active Filters')}>
            <p>Active Filters ({activeFilters.length})</p>
            {isOpen('Active Filters') ? <FontAwesomeIcon icon={faMinus} className={styles.icon}/> : <FontAwesomeIcon icon={faPlus} className={styles.icon}/>}
          </div>

          <div className={activeFilters.length > 0 && isOpen('Active Filters') || isOpen('Active Filters') ? `${styles.sectionContent} ${styles.open}`: `${styles.sectionContent}`}>
            
            {activeFilters.length < 1 ? <p className={styles.message}>You haven't applied any filters yet.</p> : <></>}

            <div className={styles.filtersContainer}>
              {activeFilters.map((filter) => {
                return (
                  <div key={`${filter.section}:${filter.option}`} className={styles.activeFilter}>
                    <p>{filter.section}: </p>
                    <p>{filter.option}</p>
                    <FontAwesomeIcon icon={faTimes} className={styles.removeIcon} onClick={() => removeActiveFilter(filter)}/>
                </div>
                )
              })}
            
            </div>
          </div>

        </div>

        {filters.sections.map((filter) => {
          return (
            <div key={filter.name} className={styles.filterSection}>
              <div className={styles.sectionHeader} onClick={() => toggleOpen(filter.name)}>
                <p>{filter.name}</p>
                {isOpen(filter.name) ? <FontAwesomeIcon icon={faMinus} className={styles.icon}/> : <FontAwesomeIcon icon={faPlus} className={styles.icon}/>}
              </div>

              <div className={isOpen(filter.name) ? `${styles.sectionContent} ${styles.open}`: `${styles.sectionContent}`}>

                {filter.options.map((option) => {
                  return (
                    <div key={`option:${filter.name}:${option}`} 
                    className={isActive(`${filter.name}:${option}`) ? `${styles.filterChoice} ${styles.active}` : `${styles.filterChoice}`}
                    onClick={() => toggleActive(`${filter.name}:${option}`)}>

                    

                      <div className={styles.checkbox}>
                        <FontAwesomeIcon icon={faCheck} className={styles.checkmark} />
                      </div>

                      <p className={styles.label}>{option}</p>
                    </div>
                  )
                })}
              </div>
          </div>
          )
        })}

        

        <div className={styles.footer}>
          <button className={styles.resetBtn} onClick={() => setActiveFilters([])}>Reset Filters</button>
          <button className={styles.closeBtn} onClick={() => params.setShowFilter(false)}>Close</button>
        </div>
      </div>
    </div>
  )
};

【问题讨论】:

  • 您在比较对象,而不是对象属性。对象相等性由引用确定,而不是由形状/值确定,请参阅:How to determine equality for two JavaScript objects?。您需要单独比较所有相关属性以获得您期望的结果。
  • 对象不是按值比较,而是我的参考(内存分配)。即使是不同变量中的相同对象也将匹配,因为与按值评估的基元不同,对象是按引用的。此外 - 您可以轻松地在 json 对象上使用 JSON.parse(obj) 而不是拆分子字符串。
  • 对此的快速修复将是:currentActiveFilters.findIndex(activefilter =&gt; JSON.stringify(activefilter) === JSON.stringify(filter))。检查这个主题 - Object comparison in JavaScript
  • 仅当它们的顺序相同时,最好明确地这样做let index = currentActiveFilters.findIndex(({section, option}) =&gt; section === filter.section &amp;&amp; option === filter.option);
  • 这表明您传递的引用在您删除时匹配。

标签: javascript arrays reactjs next.js


【解决方案1】:

正如 cmets 中所解释的,您无法比较对象是否相等,除非它们引用相同的变量。 this question中有一些深度对象比较的方法。

但是,一种常见的方法是为您的对象数组添加一个唯一标识符。然后就可以findIndex的对象匹配到相同的标识符了。

[
    {
        "id": "filter1",
        "section": "Section 1",
        "option": "Option 1"
    },
    {
        "id": "filter2",
        "section": "Filter Section",
        "option": "Option 1"
    },
    {
        "id": "filter3",
        "section": "Section 1",
        "option": "Option 2"
    }
]
currentActiveFilters.findIndex(activefilter => activefilter.id === filter.id);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-27
    • 2017-10-26
    相关资源
    最近更新 更多