如何使用redux、immer和react从数组中删除元素

4
import produce from "immer";

const initialState = {
  isLoading: true,
  error: "",
  burgers: [],
};

export default function (state = initialState, action) {
  switch (action.type) {
    case "ADD_BURGER_BUCKET": {
      return produce(state, (draftState) => {
        if (Array.isArray(action.payload)) {
          draftState.burgers.push(...action.payload);
        } else {
          draftState.burgers.push(action.payload);
        }
      });
    }
    case "REMOVE_BURGERS_BUCKET": {
      return produce(state, (draftState) => {
        draftState.burgers = []
      });
    }
    **case "REMOVE_ONE_BURGER_BUCKET": {
      return produce(state, (draftState) => {
        console.log(action.payload, draftState.burgers) console.log => 3 Proxy {0: {…}}
        draftState.burgers.filter(el => el.id !== action.payload)
      })
    }** HERE THIS ONE DOES NOT WORK!!!
    default:
      return state;
  }
}


return ( <===== BURGER BUTTON
                <Burger
                  key={burger.id}
                  text={burger.name}
                  cost={burger.cost}
                  onClick={() => {
                    dispatch({
                      type: "REMOVE_ONE_BURGER_BUCKET",
                      payload: burger.id, <=== PASS ID TO REDUCER
                    }); <==== THIS ONE DOESN'T REMOVE THE ELEMENT FROM AN ARRAY
                    localStorage.setItem("burger", JSON.stringify(burger));
                    localStorage.setItem(
                      "burgersBucket",
                      JSON.stringify(
                        list.burgers.filter((el) => el.id !== burger.id)
                      )
                    );
                    history.push("/redo");
                  }}
                />
              );
            }

我想根据元素的id从数组中移除该元素,但是我无法实现,这是我在控制台中得到的输出

3 Proxy {0: {…}}

我已经导入了useSelector和useDispatch钩子。

完整代码

    import React from "react";

import { Wrapper } from "../../styled/general";
import { Menu, MenuTitle, Burgers } from "../home/homestyled";
import Burger from "../../component/burger";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import { useHistory } from "react-router-dom";

export default function Index() {
  const list = useSelector((state) => state.burgerBucket);
  const dispatch = useDispatch();
  const history = useHistory();

  useEffect(() => {
    console.log(list.burgers);
    if (list.burgers.length > 0) {
      localStorage.setItem("burgersBucket", JSON.stringify(list.burgers));
    } else {
      let burgersBucket = JSON.parse(localStorage.getItem("burgersBucket"));
      dispatch({ type: "ADD_BURGER_BUCKET", payload: burgersBucket });
    }
  }, []);

  return (
    <Wrapper>
      <Menu>
        <MenuTitle>My Bucket</MenuTitle>
        <Burgers>
          {[...list.burgers, { finish: true }, { addMore: true }].map(
            (burger) => {
              if (burger.addMore) {
                return (
                  <Burger
                    key={-2}
                    bg={"lightgreen"}
                    text={"Додати ще"}
                    onClick={() => {
                      history.push("/");
                    }}
                  />
                );
              }
              if (burger.finish) {
                return (
                  <Burger
                    key={-1}
                    bg={"#ff5050"}
                    text={"Завершити"}
                    onClick={() => {
                      dispatch({ type: "REMOVE_BURGERS_BUCKET" });
                      history.push("/");
                    }}
                  />
                );
              }
              return (
                <Burger
                  key={burger.id}
                  text={burger.name}
                  cost={burger.cost}
                  onClick={() => {
                    dispatch({
                      type: "REMOVE_ONE_BURGER_BUCKET",
                      payload: burger.id,
                    });
                    localStorage.setItem("burger", JSON.stringify(burger));
                    localStorage.setItem(
                      "burgersBucket",
                      JSON.stringify(
                        list.burgers.filter((el) => el.id !== burger.id)
                      )
                    );
                    history.push("/redo");
                  }}
                />
              );
            }
          )}
        </Burgers>
      </Menu>
    </Wrapper>
  );
}
enter code here

全代码缩减器

import produce from "immer";

const initialState = {
  isLoading: true,
  error: "",
  burgers: [],
};

export default function (state = initialState, action) {
  switch (action.type) {
    case "ADD_BURGER_BUCKET": {
      return produce(state, (draftState) => {
        if (Array.isArray(action.payload)) {
          draftState.burgers.push(...action.payload);
        } else {
          draftState.burgers.push(action.payload);
        }
      });
    }
    case "REMOVE_BURGERS_BUCKET": {
      return produce(state, (draftState) => {
        draftState.burgers = []
      });
    }
    case "REMOVE_ONE_BURGER_BUCKET": {
      return produce(state, (draftState) => {
        console.log(action.payload, draftState.burgers)
        draftState.burgers.filter(el => el.id !== action.payload)
      })
    }
    default:
      return state;
  }
}

这个也可能很重要,这是当用户点击汉堡按钮后我被重定向到的代码(页面)。

完整代码

import React, { useEffect, useState, useContext } from "react";

import { Wrapper } from "../../styled/general";
import { Menu, MenuTitle } from "../home/homestyled";
import {
  BurgerIngridients,
  IngridientWrapper,
  BurgerDetails,
  DetailsTitle,
  IngridientsDetails,
  Total,
  DetailsButtonContinue,
} from "./burgerredostyled";
import Ingridient from "../../component/Ingridient";
import IngridientdetailBlock from "../../component/IngridientsDetailBlock";

import { useHistory } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { useBurger } from "../../hooks/useBurger";

import { sumOfToppings } from "../../helpers/sum";

export default function Index() {
  const burger = useSelector((state) => state.burger);
  const [toppings, error, isLoading] = useBurger(
    "http://localhost:3000/toppings"
  );
  const dispatch = useDispatch();
  const history = useHistory();

  useEffect(() => {
    if (burger.id) {
      localStorage.setItem("burger", JSON.stringify(burger));
    } else {
      let localStorageBurger = JSON.parse(localStorage.getItem("burger"));
      dispatch({ type: "ADD_BURGER", payload: localStorageBurger });
    }

    for (let i = 0; i < toppings.length; i++) {
      for (let j = 0; j < burger.consists_of.length; j++) {
        if (burger.consists_of[j].name === toppings[i].name) {
          toppings[i].quantity = burger.consists_of[j].quantity;
        }
      }
    }

    if (toppings.length > 0) {
      dispatch({
        type: "ADD_BURGER",
        payload: { ...burger, consists_of: toppings },
      });
    }
  }, [isLoading]);

  const add = (text, num) => {
    for (let i = 0; i < toppings.length; i++) {
      if (toppings[i].name === text) {
        toppings[i].quantity = num;
      }
    }
    dispatch({
      type: "ADD_BURGER",
      payload: { ...burger, consists_of: toppings },
    });
    localStorage.setItem(
      "burger",
      JSON.stringify({ ...burger, consists_of: toppings })
    );
  };

  if (!isLoading)
    return (
      <Wrapper>
        <Menu>
          <MenuTitle>{burger && burger.name}</MenuTitle>
          <IngridientWrapper>
            <BurgerIngridients>
              {burger.consists_of.map(({ name, quantity, id }) => {
                return (
                  <Ingridient
                    key={id}
                    text={name}
                    add={add}
                    initValue={quantity}
                  />
                );
              })}
            </BurgerIngridients>
            <BurgerDetails>
              <DetailsTitle>
                <span>{burger && burger.name} | інформація</span>{" "}
                <DetailsButtonContinue
                  onClick={() => {
                    dispatch({
                      type: "ADD_BURGER_BUCKET",
                      payload: { ...burger, cost: sumOfToppings(burger) },
                    });
                    history.push("/bucket");
                  }}
                >
                  продовжити
                </DetailsButtonContinue>
              </DetailsTitle>
              <IngridientsDetails>
                {burger.consists_of
                  .filter((el) => el.quantity > 0)
                  .map((el) => {
                    return (
                      <IngridientdetailBlock
                        key={el.id}
                        text={el.name}
                        price={el.quantity * el.cost}
                        qty={el.quantity}
                      ></IngridientdetailBlock>
                    );
                  })}
              </IngridientsDetails>
              <Total>Загалом {sumOfToppings(burger)}(грн.)</Total>
            </BurgerDetails>
          </IngridientWrapper>
        </Menu>
      </Wrapper>
    );

  if (isLoading) {
    return <span>loading</span>;
  }
}
1个回答

7

Array.prototype.filter 不会改变数组,而是创建一个新的数组。

所以这个:

draftState.burgers.filter(el => el.id !== action.payload)

实际上并没有改变 draftState.burgers。但下面这个会改变:

produce(state, (draftState) => {
  draftState.burgers = draftState.burgers.filter(el => el.id !== action.payload)
})

有没有一种更简短的语法来实现这个功能?比如基于条件进行原地切割。 - Avin Kavish
任何改变draftState.burgers值的代码都是有效的。 - backtick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接