Search code examples
javascriptreactjsreduxredux-toolkit

Icon change not showing using Redux Tool


I have a situation here where there are two components. the first component have an input field and the other component is displaying icon and a title name. i used Redux toolkit where when the input field is written anything the icon is changed to another icon. the problem is the icon doesn't change i don't why so, i want to know how to define the icon in the component as shown in my code below.

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

const initialState = {
  icon: "unTick",
  value: "",
};

const tickSlice = createSlice({
  name: "tickSign",
  initialState,
  reducers: {
    setValue: (state, action) => {
      state.value = action.payload;
    },
    changeIcon: (state) => {
      if (state.icon === "unTick") {
        state.icon = "tick";
      } else {
        state.icon = "unTick";
      }
    },
  },
});

export const { changeIcon, setValue } = tickSlice.actions;
export const selectValue = (state) => state.value;
export const selectIcon = (state) => state.icon;

export default tickSlice.reducer;

the code of the first component of input

  const value = useSelector(selectValue);
  const dispatch = useDispatch();

  const handleChange = useCallback(
    (e) => {
      dispatch(setValue(e.target.value));
      if (e.target.value.trim().length !== 0) {
        dispatch(changeIcon("tick"));
      } else {
        dispatch(changeIcon("unTick"));
      }
    },
    [dispatch]
  );
<input
            id="boxInput"
            type="text"
            value={value}
            placeholder="Enter Apprenticeship Title"
            onChange={handleChange}
          />

the code for the icon change component

import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { selectIcon } from "../features/TickSlice";
import { TickCircle, UnTickCircle } from "./IconSvg";

const DescNavTitles = ({ title }) => {
  const icon = useSelector(selectIcon);
  const dispatch = useDispatch();

  return (
    <div className="navTitle">
      <svg>{icon === "tick" ? <TickCircle /> : <UnTickCircle />}</svg>
      <p className="navTitle__name">{title}</p>
    </div>
  );
};

export default DescNavTitles;

So, i don't know where exactly should i define the icon to be displayed and changed dynamically

components on the page

Redux tool when written

Redux tool when not written


Solution

  • You are not selecting the icon state correctly. Based on the screen captures showing the Redux state from the devtools your root state has two properties, appren and tick.

    The Redux state:

    {
      appren: {
        boxItems: [....],
        title: "",
        position: "",
        ...
      },
      tick: {
        icon: "....",
        value: "...."
      },
    }
    

    I'm assuming it's this state.tick that is the tickSign slice you are working with. Recall that the useSelector hook callback is passed the entire Redux state object.

    const icon = useSelector(state => state.tick.icon);
    

    The selectValue and selectIcon selector functions need to access the correct path to access the expected state properties.

    export const selectValue = (state) => state.tick.value;
    export const selectIcon = (state) => state.tick.icon;
    

    Additionally, the changeIcon action is only toggling the state value when dispatched, but it seems you want to conditionally set the value from an input element's onChange handler. I suggest the following refactor:

    const initialState = {
      isTicked: false,
      value: "",
    };
    
    const tickSlice = createSlice({
      name: "tickSign",
      initialState,
      reducers: {
        setValue: (state, action) => {
          state.value = action.payload;
        },
        changeIsTicked: (state, action) => {
          state.isTicked = action.payload;
        },
      },
    });
    
    export const { changeIcon, setValue } = tickSlice.actions;
    export const selectValue = (state) => state.tick.value;
    export const selectIsTicked = (state) => state.tick.isTicked;
    
    const value = useSelector(selectValue);
    const dispatch = useDispatch();
    
    const handleChange = useCallback((e) => {
      dispatch(setValue(e.target.value));
      dispatch(changeIsTicked(e.target.value.trim().length !== 0));
    }, [dispatch]);
    
    <input
      id="boxInput"
      type="text"
      value={value}
      placeholder="Enter Apprenticeship Title"
      onChange={handleChange}
    />
    
    const DescNavTitles = ({ title }) => {
      const isTicked = useSelector(selectIsTicked);
    
      return (
        <div className="navTitle">
          <svg>{isTicked ? <TickCircle /> : <UnTickCircle />}</svg>
          <p className="navTitle__name">{title}</p>
        </div>
      );
    };
    

    The isTicked state is actually unnecessary though as it can be completely derived from the state.tick.value state value. You can create a selector function that computes this. Example:

    const initialState = {
      value: "",
    };
    
    const tickSlice = createSlice({
      name: "tickSign",
      initialState,
      reducers: {
        setValue: (state, action) => {
          state.value = action.payload;
        },
      },
    });
    
    export const { changeIcon, setValue } = tickSlice.actions;
    export const selectValue = (state) => state.tick.value;
    export const selectIsTicked = (state) => !!state.tick.value.length;
    
    const DescNavTitles = ({ title }) => {
      const isTicked = useSelector(selectIsTicked);
    
      return (
        <div className="navTitle">
          <svg>{selectIsTicked ? <TickCircle /> : <UnTickCircle />}</svg>
          <p className="navTitle__name">{title}</p>
        </div>
      );
    };