Redux is able to set characters to an array however the remove action does not seem to reach the reducer

Viewed 31

I am using redux to update an array of characters as a user types or erases it, so that when the user correctly types the entire phrase I can set a success flag.

So far when typing in characters the redux type SET_INPUT fires off and updates my state but unfortunately my REMOVE_INPUT doesn't seem to fire off but it does however reach the action.

My Reducer: import { GET_PHRASE, SET_LOADING, SET_INPUT, REMOVE_INPUT } from "../types";

const initialState = {
    level: 1,
    phrase: null,
    scrambledPhrase: null,
    words: [],
    input: [],
    goal: [],
    success: false,
    loading: false,
};

export const phraseReducer = (state = initialState, action) => {
    switch (action.type) {
        case GET_PHRASE:
            return {
                ...state,
                phrase: action.payload.sentence,
                scrambledPhrase: action.payload.scrambledPhrase,
                words: action.payload.phrase.split(" "),
                goal: action.payload.phrase.split(""),
                loading: false,
            };
        case SET_INPUT:
            console.log("setting input");
            return {
                ...state,
                input: [...state.input, action.payload],
            };
        case REMOVE_INPUT:
            console.log("removing input");
            return {
                ...state,
                input: [...state.input.slice(0, -1)],
            };
        case SET_LOADING:
            return {
                ...state,
                loading: true,
            };
        default:
            return state;
    }
};

My Actions:

import { GET_PHRASE, SET_LOADING, SET_INPUT, REMOVE_INPUT } from "../types";
import axios from "axios";

export const getPhrase = (level) => async (dispatch) => {
    try {
        setLoading();

        await axios
            .get(`MY ROUTE`)
            .then((res) => {
                // console.log(res);
                const sentence = res.data.data.phrase;
                const scrambledSentence = scramblePhrase(
                    res.data.data.phrase
                );

                dispatch({
                    type: GET_PHRASE,
                    payload: {
                        phrase: phrase.toLowerCase(),
                        scrambledPhrase: scrambledPhrase.toLowerCase(),
                    },
                });
            });
    } catch (err) {
        console.error(err);
    }
};

// SET INPUT
export const setInput = (input) => async (dispatch) => {
    try {
        dispatch({
            type: SET_INPUT,
            payload: input,
        });
    } catch (err) {
        console.error(err);
    }
};

// REMOVE INPUT
export const removeInput = () => {
    try {
        console.log("remove reached in actions");
        return {
            type: REMOVE_INPUT,
        };
    } catch (err) {
        console.error(err);
    }
};

// SET LOADING
export const setLoading = () => {
    console.log("Loading...");
    return {
        type: SET_LOADING,
    };
};

My Component to input a character:

import React, { useState } from "react";

// redux imports
import { connect } from "react-redux";
import { setInput, removeInput } from "../redux/actions/phraseActions";
import PropTypes from "prop-types";

const Character = ({ character, hasSpace, setInput }) => {
    const [success, setSuccess] = useState();

    const handleChange = (e) => {
        if (e.target.value === character) {
            // console.log("Success");
            setSuccess(true);
        } else {
            setSuccess(false);
        }
    };

    const keyedDown = (e) => {
        // check for space or a letter
        if (e.keyCode === 32 || (e.keyCode > 64 && e.keyCode < 91)) {
            setInput(String.fromCharCode(e.keyCode).toLowerCase());
        }
        // check for backspace
        else if (e.keyCode === 8) {
            removeInput();
        }
    };
    return (
        <div
            className={`character ${
                success ? "success" : hasSpace ? "space" : ""
            }`}
        >
            <input
                type="text"
                name="name"
                required
                maxLength="1"
                size="1"
                onChange={handleChange}
                onKeyDown={keyedDown}
                className="input"
                autoComplete="off"
            ></input>
        </div>
    );
};

Character.propTypes = {
    setInput: PropTypes.func.isRequired,
    removeInput: PropTypes.func.isRequired,
};

const mapStateToProps = (state) => ({
    // define state
    phrase: state.phrase,
});

export default connect(mapStateToProps, { setInput, removeInput })(Character);

In my console you can see which points I am reaching: enter image description here

1 Answers

In your event handler you are not calling removeInput that was provided by connect (props.removeInput) but the imported removeInput that doesn't dispatch anything and just returns an action object, so I suggest changing the component definition to:

const Character = ({ character, hasSpace, setInput, removeInput }) => {

In your reducer you can do: input: state.input.slice(0, -1), because slice already returns a shallow copy of the array so no need to copy it with [...]

Related