繁体   English   中英

为什么我的 React 组件在按 useState() 控制的值进行过滤时显示不正确的数据?

[英]Why does my React component display incorrect data when filtering by values controlled by useState()?

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

我有一个菜单并单击每个项目调用 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() 的默认值。 从菜单中选择不会显示正确类别的组件。

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

任何人有任何想法如何使用这样的菜单来过滤数据,然后只显示基于过滤数据的特定组件?

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

常量.js:

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

data.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 <p>{object_.title}</p>; 渲染出您的组件,例如<ProjectCardContainer project={object_} />

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

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>
  );
};

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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