Removing a value from an array using redux toolkit
Asked Answered
A

4

9

I am new to using "@reduxjs/toolkit" (version "^1.5.1").

I am trying to remove an object from within the state's array (roundScore). This is usually something that is very simple to do using filter(). For some reason this isn't working and I can't figure out why. Here's my code:

Reducer slice:

import { createSlice } from "@reduxjs/toolkit";

export const roundScoreSlice = createSlice({
    name: "roundScore",
    initialState: {
        roundScore: [],
    },

    reducers: {
        deleteArrow: (state, action) => {
            console.log(`action.payload = ${action.payload}`); // returns correct id

            state.roundScore.filter((arrow) => arrow.id !== action.payload);
        },
    },
});

export const { deleteArrow } = roundScoreSlice.actions;

export default roundScoreSlice.reducer;

React component:

import React from "react";
import styled from "styled-components";
import { motion } from "framer-motion";
import { useDispatch } from "react-redux";
import { deleteArrow } from "../../redux-reducers/trackSession/roundScoreSlice";

export default function InputtedScore({
    arrowScore,
    id,
    initial,
    animate,
    variants,
}) {
    const dispatch = useDispatch();

    const applyStyling = () => {
        switch (arrowScore) {
            case 0:
                return "miss";
            case 1:
                return "white";
            case 2:
                return "white";
            case 3:
                return "black";
            case 4:
                return "black";
            case 5:
                return "blue";
            case 6:
                return "blue";
            case 7:
                return "red";
            case 8:
                return "red";
            case 9:
                return "gold";
            case 10:
                return "gold";
            default:
                return null;
        }
    };

    return (
        <ParentStyled
            id={id}
            initial={initial}
            animate={animate}
            variants={variants}
            onClick={() => dispatch(deleteArrow(id))}
        >
            <Circle className={applyStyling()}>
                {arrowScore}
                <IconStyled>
                    <IoIosClose />
                </IconStyled>
                <IoIosClose className="redCross" />
            </Circle>
        </ParentStyled>
    );
}

The state after adding 2 arrows would look like this:

roundScore: [
    {
        id:"e0f225ba-19c2-4fd4-b2bf-1e0aef6ab4e0"
        arrowScore:7
    },

    {
        id:"2218385f-b37a-4f2c-a8db-4e7e65846171"
        arrowScore:5
    }
]

I've tried a combination of things.

  • Using e.target.id within dispatch
  • Using e.currentTarget.id within dispatch
  • Using ({id}) instead of just (id) within dispatch
  • Wrapping the reducer function with or without braces e.g. within (state, action) => { /* code */ }

What is it I'm missing? I know this is going to be a simple fix but for some reason it's eluding me.

Any help is much appreciated.

Adley answered 7/5, 2021 at 14:35 Comment(1)
Do you try to add return filter in deleteArrow?Annikaanniken
K
30

Okay, it looks like the issue is in the way how filter method works, it returns a new array, and an initial array is not mutated (that's why we have been using filter before to manipulate redux state), also in the code you've shown value of the filtering operation not assigned to any property of your state You need to assign the value or mutate array, so the code below should work for you

state.roundScore = state.roundScore.filter((arrow) => arrow.id !== action.payload);

Mutate your existing array:

state.roundScore.splice(state.roundScore.findIndex((arrow) => arrow.id === action.payload), 1);
Keyway answered 7/5, 2021 at 16:29 Comment(4)
Thank you. The first suggestion works perfectly. With your second suggestion, the "a" (as in "a.findIndex") is not defined.Adley
Sure, instead of a it should be an array, you are trying to mutate state.roundScore.findIndex((arrow)Keyway
You have mistake. state.roundScore.splice(state.roundScore.findIndex((arrow) => arrow.id === action.payload), 1);Invertebrate
Current splice solution contains a potential bug. If for some reason state will not contain an element with id you are trying to remove, findIndex would return -1. That will result in splice(-1, 1) call. And last element of the array will be deleted.Hamblin
W
2

We can think outside of the box and look at it from another way. the React component above is just actually a child of a certain parent component. And for the purpose of my answer i assume that in your parent component you have some form of array.map .

So from that code, each array item will already have an array index. and you can pass that index as a prop to the above react component like so:

const InputtedScore = ({ ...all your props, id, inputIndex }) => {
    const dispatch = useDispatch();
    // Having access to your index from the props, you can even
    // find the item corresponding to that index
    const inputAtIndex_ = useSelector(state => state.input[inputIndex])
    

    const applyStyling = () => {
        switch (arrowScore) {
            ...your style logic
        }
    };

    return (
        // you can send that index as a payload to the reducer function
        <ParentStyled id={id} onClick={() => dispatch(deleteArrow(inputIndex))} 
            ...the rest of your properties >
            <Circle className={applyStyling()}>
                {arrowScore}
                <IconStyled>
                    <IoIosClose />
                </IconStyled>
                <IoIosClose className="redCross" />
            </Circle>
        </ParentStyled>
    );
}

After dispaching the delete action by sending as payload the item's index already, you do not need to find the item in the reducer anymore:

deleteMeal: (state, action) => {
    // you receive you inputIndex from the payload
    let { inputIndex } = action.payload;
    // and you use it to splice the desired item off the array
    state.meals.splice(inputIndex, 1);
    ...your other logic if any
},
Whitethorn answered 22/12, 2021 at 2:26 Comment(0)
C
0

You need to mutate your existing array

state.roundScore.splice(state.roundScore.findIndex((arrow) => arrow.id === action.payload), 1);
Castilian answered 23/11, 2022 at 7:6 Comment(0)
A
0
Buddy you are not returning inside state
either write 

return  state.roundScore.filter((arrow) => arrow.id !== action.payload);

or 

state.roundScore=  state.roundScore.filter((arrow) => arrow.id !== action.payload);

i hope it helps

Ageratum answered 18/9, 2023 at 4:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.