Почему мой компонент React отображает неверные данные при фильтрации по значениям, контролируемым useState ()? - PullRequest
0 голосов
/ 03 ноября 2019

У меня есть список объектов, которые я хотел бы передать в функцию карты, которая передает каждый объект в качестве реквизита для компонента, подлежащего визуализации.

У меня есть меню, и нажатие каждого элемента вызывает setActiveItem () обновление activeItem, которое управляется хуком useState.

Я пытаюсь отфильтровать список объектов на основе этого значения 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, но я не уверен, как обойти эту проблему. Я немного новичок в использовании хуков, но это похоже на проблему, которая должна часто решаться.

Кто-нибудь Есть идеи, как я могу использовать подобное меню для фильтрации данных, а затем отображать только определенные компоненты на основе отфильтрованных данных?

Ответы [ 2 ]

0 голосов
/ 04 ноября 2019

В итоге проблема заключалась в том, что я не предоставлял уникальный ключ при отображении списков компонентов. Решением является предоставление уникального ключа, например, так:

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

В моем случае я знаю, что названия будут уникальными, поэтому это работает.

0 голосов
/ 03 ноября 2019

Я не думаю, что нам нужно слишком много возиться со сложным управлением состоянием. Я обновил базовый вариант в соответствии с вашими потребностями:

Constants.js:

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

data.js: import {ALL, DATA_SCIENCE, WEB_DEV, BASH} из './Constants';

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_} />

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...