React redux — отправка нового элемента в массив состояний не работает

#javascript #reactjs #react-redux

Вопрос:

У меня возникла проблема с добавлением нового элемента в переменную массива состояний. В компоненте «Раздел категорий» я вызываю .push (), чтобы добавить «new_category» в массив «категории», но ничего не происходит, даже ошибки.

Когда я пытаюсь создать новую категорию, вызвав «handleAddCategory», которая запускает отправку для «createCategory», я вижу в Redux DevTools, что она была добавлена в состояние «элемент», как только я получу данные из своего бэкенда, но затем, когда .push() вызывается в компоненте «Раздел категорий», как я уже говорил, ничего не происходит.

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

Редуктор категории

 const initialState = {
  items: [],
  item: {},
}

const reducer = (state=initialState, action) => {
  switch(action.type) {
    case GET_CATEGORIES:
      return {
        ...state,
        items: action.payload
      }

    case CREATE_CATEGORY:
      return {
        ...state,
        item: action.payload
      }
    

    default:
        return state;
  }
};

export default reducer;
 

Действия категории

 export const getCategories = () => {
  return async(dispatch) => {
    const res = await fetch('/api/get-categories/', {
      method: 'GET',
      headers: {
        'X-CSRFToken': getCookie('csrftoken'),
        'Content-Type': 'application/json'
      }
    });
    const data = await res.json();
    dispatch({
      type: GET_CATEGORIES,
      payload: data
    });
  }
}

export const createCategory = (name) => {
  return async(dispatch) => {
    const res = await fetch('/api/manage-category/', {
      method: 'POST',
      headers: {
        'X-CSRFToken': getCookie('csrftoken'),
        'Content-type': 'application/json'
      },
      body: JSON.stringify({'name': name})
    });
    const data = await res.json();
    dispatch({
      type: CREATE_CATEGORY,
      payload: data
    });
  }
}
 

Компонент категоризации

Здесь я пытаюсь вызвать .push во втором эффекте использования, потому что sumTasks() должен вызываться только при изменении «категорий», но, к сожалению, он вообще не нажимает…

 import React from 'react'
import CategoryItem from './CategoryItem'
import AddPopover from '../popovers/AddPopover'
import { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getTasks } from '../../state/action-creators/taskActions';
import { createCategory, setActiveCategory } from '../../state/action-creators/categoryActions';

const CategorySection = () => {
  const dispatch = useDispatch();

  const categories = useSelector((state) => state.category.items);
  const new_category = useSelector((state) => state.category.item);
  const active_category = useSelector((state) => state.category.active);

  const [anchorEl, setAnchorEl] = useState(null);
  const [totalTasks, setTotalTasks] = useState(0);
  const open = Boolean(anchorEl);

  useEffect(() => {
    sumTasks();
  }, [categories])

  useEffect(() => {
    categories.push(new_category);
    
  }, [new_category])

  function sumTasks() {
    let sum = categories.reduce((total, current) => total = total   current.total_tasks, 0);
    setTotalTasks(sum);
  }

  const onCategoryClick = async (id) => {
    if(active_category != id) {
      await dispatch(setActiveCategory(id));
      await dispatch(getTasks());
    }
  }

  const single_category = categories amp;amp; categories.map((category) => {
    return(
      <CategoryItem key={category.id} category={category} active_category={active_category} onCategoryClick={onCategoryClick} />
    )
  })

  const handleClick = (event) => {
    setAnchorEl(event.currentTarget);
  };
  
  const handleClose = () => {
    setAnchorEl(null);
  };

  const handleAddCategory = async (e, value) => {
    if(e.keyCode === 13 amp;amp; value != '') {
      setAnchorEl(null);
      await dispatch(createCategory(value));
    }
  }
  
  return (
    <div className="bg-dark mb-3" id="category-wrapper">
      <div className="container mt-2 mb-2">
        <div className="row">
          <span className="d-flex justify-content-between">
            <div className="fs-4 text-white">
              Categories
            </div>
            <div className="category-tooltip-div" data-bs-toggle="tooltip" data-bs-placement="right" title="">
              <svg onClick={handleClick} xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="orange" id="sidebar-add-category"
                className="bi bi-plus-square rename-add-icon category-add-icon global-add-category-icon" viewBox="0 0 16 16">
                <path
                  d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z" />
                <path
                  d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
              </svg>
              <AddPopover 
                open={open}
                anchorEl={anchorEl}
                horizontal={30}
                onClose={handleClose}
                placeholder="Enter Category name"
                handlePressEnter={handleAddCategory}
              />
            </div>
          </span>
        </div>
      </div>
      <div className="container" id="sidebar-categories">
        <li className={`row hovered-nav-item active-category ${active_category == -1 ? "item-selected" : ""}`}>
          <span onClick={() => onCategoryClick(-1)} className="category-link category-item fs-5 d-flex " id="sidebar-all-tasks" value="-1">
            <div className="all-tasks-text">All</div>

            {totalTasks > 0 ? (
              <div className="total-number">
                <div className="number">{totalTasks}</div>
              </div>
            ):''}

          </span>
        </li>
        <div id="categories-loop">
          {single_category}
        </div>
      </div>
    </div>
  )
}

export default CategorySection
 

Комментарии:

1. Вы не должны изменять категории с помощью push, но создайте действие, которое добавляет неизменяемый элемент в массив

2. Я изучил возможность добавления в массив состояний и опубликовал ответ ниже. Дело в том, что я просматривал учебник Трэвиса по медиа-редактированию на YT, и он использовал .push (), чтобы добавить новую запись в массив, и это работало…

3. Возможно, он использовал immer в этом контексте с набором инструментов redux, может быть? Но все еще в действии, вероятно?

Ответ №1:

Я изменил редуктор CREATE_CATEGORY на:

 case CREATE_CATEGORY:
      return {
        ...state,
        items: [...state.items, action.payload]
      }