Reputation: 171
I have a list of trainers and I have to select and unselect them as a toggle button. Below is the code to do so but I don't know how to implement it correctly.
Action:
const selectTrainer = (selected) => ({
type: 'SELECT_TRAINER',
payload: {
selected: selected
}
});
Trainer:
function Trainer(props) {
const [addBtnToggle, setBtnToggle] = useState(false);
return (
<>
{addBtnToggle ? <button onClick={() => setBtnToggle(!addBtnToggle)}>Selected</button> : <button onClick={() => setBtnToggle(!addBtnToggle)}>Select</button> }
{addBtnToggle && <Select />}
</>
);
}
export default Trainer;
Reducer:
import {v4 as uuid} from 'uuid';
const initalState = [
{
id: uuid(),
name: '',
team: [],
selected: false
}
];
let copyState = null;
let index = 0;
const trainerReducer = (state = initalState, action) => {
const {type, payload} = action
case "SELECT_TRAINER":
return state.map((trainer) =>
trainer.id === payload.id ? trainer.selected = true : trainer
);
default:
return state;
}
};
export default trainerReducer;
Select:
function Select() {
const dispatch = useDispatch();
const allTrainers = useSelector((state) => state.trainers);
allTrainers.map(a => a.selected===true );
const arr = []
arr.push(props.data.name)
console.log(result)
dispatch(actions.selectTrainer());
return null
}
export default Select;
How do I add a toggle to unselect a particular item and how do I unselect the already selected item when I want to select another item?
Upvotes: 1
Views: 337
Reputation: 202761
When using what I refer to as legacy reducers (i.e. pre-redux-toolkit) you need to apply the Immutable Update Pattern. The reducer function is correctly creating a shallow copy of the state
array, but it also needs to shallow copy any nested state it is updating. In this case it should create a new "trainer" object reference with the copied/updated properties.
Example:
const initialState = [
{
id: uuid(),
name: '',
team: [],
selected: false
}
];
const trainerReducer = (state = initialState, action) => {
const {type, payload} = action
switch(type) {
case "SELECT_TRAINER":
return state.map((trainer) => // <-- shallow copy array
trainer.id === payload.id
? { // <-- new object reference
...trainer, // <-- shallow copy trainer properties
selected: true, // <-- overwrite property with new value
}
: trainer
);
... other cases...
default:
return state;
}
};
How do I add a toggle to unselect a particular item and how do I unselect the already selected item when I want to select another item?
This sounds like you want only a single selected trainer. You can update the reducer case to also check if the current trainer object is selected and deselect it.
case "SELECT_TRAINER":
return state.map((trainer) =>
trainer.id === payload.id
? { ...trainer, selected: true }
: trainer.selected
? { ...trainer, selected: false }
: trainer
);
or more DRY
case "SELECT_TRAINER":
return state.map((trainer) =>
trainer.id === payload.id || trainer.selected
? { ...trainer, selected: trainer.id === payload.id }
: trainer
);
You will need to dispatch a trainer's id
with the action. For this I suggest moving the redux logic from the Select
component into the Trainer
component so it computes the selected trainer value and directly dispatches the selectedTrainer
action with the current trainer's id.
function Trainer(props) {
const dispatch = useDispatch();
const allTrainers = useSelector((state) => state.trainers);
const isSelected = allTrainers.some(trainer => trainer.selected);
return (
<button onClick={() => dispatch(actions.selectTrainer(props.id))}>
{isSelected ? "Selected" : "Select"}
</button>
);
}
To avoid needing to shallow copy the trainers array it may make more sense to just store a selected trainer id as a separate chunk of state.
Example:
const initialState = {
trainers: [
{
id: uuid(),
name: '',
team: [],
}
],
selected: null
};
const trainerReducer = (state = initialState, action) => {
const {type, payload} = action
switch(type) {
case "SELECT_TRAINER":
return {
...state,
selected: state.selected === payload.id ? null : payload.id
}
... other cases...
default:
return state;
}
};
...
function Trainer(props) {
const dispatch = useDispatch();
const selected = useSelector((state) => state.trainers.selected);
return (
<button onClick={() => dispatch(actions.selectTrainer(props.id))}>
{selected === props.id ? "Selected" : "Select"}
</button>
);
}
Upvotes: 2