'Removing a value from an array using redux toolkit

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.



Solution 1:[1]

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

Solution 2:[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
},

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 vially