【问题标题】:Why does my React component display incorrect data when filtering by values controlled by useState()?为什么我的 React 组件在通过 useState() 控制的值进行过滤时显示不正确的数据?
【发布时间】:2019-11-03 19:01:59
【问题描述】:

我有一个对象列表,我想传递给 map 函数,该函数将每个对象作为道具传递给要渲染的组件。

我有一个菜单,单击每个项目调用 setActiveItem() 更新由 useState 挂钩管理的 activeItem。

我正在尝试根据这个 activeItem 值过滤对象列表。我已经创建了一个试图复制问题的基本案例,但我的基本案例完美无缺,尽管它至少可以阐明我正在尝试这样做:

import React, { useState } from 'react';
import { Menu } from 'semantic-ui-react';

const [ALL, NUMBER, LETTER] = ['All', 'Number', 'Letter'];
const data = [
  {
    tags: [ALL, NUMBER],
    value: '1'
  },
  {
    tags: [ALL, LETTER],
    value: 'a'
  },
  {
    tags: [ALL, NUMBER],
    value: '2'
  },
  {
    tags: [ALL, LETTER],
    value: 'b'
  },
  {
    tags: [ALL, NUMBER],
    value: '3'
  },
  {
    tags: [ALL, LETTER],
    value: 'c'
  },
  {
    tags: [ALL, NUMBER],
    value: '4'
  },
  {
    tags: [ALL, LETTER],
    value: 'd'
  }
];

const renderData = (allValues, filterTag) => {
  let filteredList = allValues.filter(val => {
    return val['tags'].includes(filterTag);
  });

  return (
    <div>
      {filteredList.map(object_ => {
        return object_.value;
      })}
    </div>
  );
};

const BaseCase = props => {
  const [activeItem, setActiveItem] = useState(ALL);
  return (
    <div>
      <Menu inverted stackable fluid widths={4}>
        <Menu.Item
          name={ALL}
          active={activeItem === ALL}
          onClick={(e, { name }) => setActiveItem(name)}
        />
        <Menu.Item
          name={NUMBER}
          active={activeItem === NUMBER}
          onClick={(e, { name }) => setActiveItem(name)}
        />
        <Menu.Item
          name={LETTER}
          active={activeItem === LETTER}
          onClick={(e, { name }) => setActiveItem(name)}
        />
      </Menu>
      <div>{renderData(data, activeItem)}</div>
    </div>
  );
};

export default BaseCase;

单击数字仅显示数字,其他一切都按预期工作。现在我的组件不起作用。我将数据保存在一个单独的文件中,如下所示:

import { BASH, DATA_SCIENCE, WEB_DEV, ALL } from '../constants';
const data = [
  {
    tags: [ALL],
    title: 'Concussion App for Athletes',
     .
     .
     .
  },
  {
    tags: [DATA_SCIENCE, ALL],
    title: 'Deep Learning: Exploring Car Value with an ANN',
    ...
  },
  .
  .
  .
];
export default data;

这是我的组件。我尝试了一些注释掉的代码,但也显示了不正确的组件。

import React, { useState } from 'react';
import ProjectCardContainer from '../../containers/ProjectCardContainer';
import { Menu } from 'semantic-ui-react';
import { ALL, BASH, DATA_SCIENCE, WEB_DEV } from './constants';
import data from './project_data';
import './Projects.scss';

const styles = {
  container: {
    display: 'flex',
    justifyContent: 'space-around'
  },
  columns: {
    display: 'flex',
    flexDirection: 'column',
    marginTop: '11px'
  }
};

const renderColumn = (projectList, filterTag) => {
  let projects = projectList.filter(proj => {
    return proj['tags'].includes(filterTag);
  });

  return (
    <div style={styles.columns}>
      {projects.map(project => {
        return <ProjectCardContainer project={project} />;
      })}
    </div>
  );
};

const Projects = () => {
  const [activeItem, setActiveItem] = useState(ALL);

//   const [, updateState] = React.useState();
//   const forceUpdate = useCallback(() => updateState({}), []);

//   useEffect(() => {
//     setTimeout(forceUpdate, 100);
//   }, [activeItem]);

  return (
    <div>
      <div className='second-nav-container'>
        <Menu inverted stackable fluid widths={4}>
          <Menu.Item
            name={ALL}
            active={activeItem === ALL}
            onClick={(e, { name }) => setActiveItem(name)}
          />
          <Menu.Item
            name={WEB_DEV}
            active={activeItem === WEB_DEV}
            onClick={(e, { name }) => setActiveItem(name)}
          />
          <Menu.Item
            name={DATA_SCIENCE}
            active={activeItem === DATA_SCIENCE}
            onClick={(e, { name }) => setActiveItem(name)}
          />
          <Menu.Item
            name={BASH}
            active={activeItem === BASH}
            onClick={(e, { name }) => setActiveItem(name)}
          />
        </Menu>
      </div>
      <div style={styles.container}>{renderColumn(data, activeItem)}</div>
    </div>
  );
};

export default Projects;

基本上,渲染的组件列表通常是不正确的,除非刷新页面并使用来自 useState() 的默认值。从菜单中选择不会显示正确类别的组件。

我认为问题在于在更新 activeItem 之前调用了渲染函数,但我不确定如何解决该问题。我对使用钩子有点陌生,但这似乎是一个必须经常出现的问题。

有人知道如何使用这样的菜单过滤数据,然后仅根据过滤后的数据显示特定组件吗?

【问题讨论】:

  • 只是为了澄清 - 你有一个带有标签列表的下拉菜单,当你选择一个特定的标签时,你想渲染来自data的所有具有该标签的对象?跨度>
  • 没关系,我看到了这个例子。 1 分钟

标签: javascript reactjs react-hooks


【解决方案1】:

我认为我们不需要过多地处理复杂的状态管理。我更新了基本案例以满足您的需求:

常量.js:

export const [ALL, DATA_SCIENCE, WEB_DEV, BASH] = ['All', 'DATA_SCIENCE', 'WEB_DEV', 'BASH'];

数据.js: 从 './Constants' 导入 {ALL, DATA_SCIENCE, WEB_DEV, BASH};

const data = [
    {
        tags: [ALL],
        title: 'Concussion App for Athletes',
      },
    {
        tags: [DATA_SCIENCE, ALL],
        title: 'Deep Learning: Exploring Car Value with an ANN',
    },
    {
        tags: [BASH, ALL],
        title: 'Bash 101'
    },
    {
        tags: [WEB_DEV, ALL],
        title: 'Web Development Book'
    },
    {
        tags: [WEB_DEV, ALL],
        title: 'Fundamentals of web design'
    }
]

export default {data};

BaseCase.js:

import React, { useState } from 'react';
import { Menu } from 'semantic-ui-react';
import data from './data';
import {ALL, DATA_SCIENCE, WEB_DEV, BASH} from './Constants';


const renderData = (allValues, filterTag) => {

  let filteredList = Object.values(allValues.data).filter(val => {
    return val['tags'].includes(filterTag);
  });

  return (
    <div>
      {filteredList.map(object_ => {
        return <p>{object_.title}</p>;
      })}
    </div>
  );
};

const BaseCase = props => {

const [activeItem, setActiveItem] = useState(ALL);

const newData = data;

  return (
    <div>
      <Menu inverted stackable fluid widths={4}>
        <Menu.Item
          name={ALL}
          active={activeItem === ALL}
          onClick={(e, { name }) => setActiveItem(name)}
        />
        <Menu.Item
          name={DATA_SCIENCE}
          active={activeItem === DATA_SCIENCE}
          onClick={(e, { name }) => setActiveItem(name)}
        />
        <Menu.Item
          name={WEB_DEV}
          active={activeItem === WEB_DEV}
          onClick={(e, { name }) => setActiveItem(name)}
        />
        <Menu.Item
          name={BASH}
          active={activeItem === BASH}
          onClick={(e, { name }) => setActiveItem(name)}
        />
      </Menu>
      <div>{renderData(newData, activeItem)}</div>
    </div>
  );
};

export default BaseCase;

return &lt;p&gt;{object_.title}&lt;/p&gt;; 渲染您的组件,如&lt;ProjectCardContainer project={object_} /&gt;

【讨论】:

  • 这正是我的问题(返回 ;需要的是唯一键,因为这些容器被呈现为列表
【解决方案2】:

最后的问题是我在渲染组件列表时没有提供唯一键。解决方案是提供一个唯一的密钥,如下所示:

const renderColumn = (projectList, filterTag) => {
  let projects = projectList.filter(proj => {
    return proj['tags'].includes(filterTag);
  });

  return (
    <div style={styles.columns}>
      {projects.map(project => {
        return <ProjectCardContainer key={project.title} project={project} />;
      })}
    </div>
  );
};

就我而言,我知道标题将是独一无二的,所以这很有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-08
    • 2020-10-11
    • 1970-01-01
    • 2016-05-29
    • 1970-01-01
    • 2019-12-20
    • 2020-06-10
    • 2021-12-19
    相关资源
    最近更新 更多