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
CodePudding user response:
In the component's render method, you are using the useSelector hook to get the current icon value from the state and conditionally render either TickCircle or UnTickCircle component based on the icon value.
const icon = useSelector(selectIcon);
return (
<div className="navTitle">
<svg>{icon === "tick" ? <TickCircle /> : <UnTickCircle />}</svg>
<p className="navTitle__name">{title}</p>
</div>
);
Try this.
CodePudding user response:
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>
);
};