I’m new to react and redux, I’m trying to do things the newish hooks way and running into issues opening and closing a Modal using a redux state.
Basically, as soon as my page loads, the modal opens, even though the initial state in the slice is set to false and the close button in the modal footer doesn’t close it.
I’m trying to learn from the example that compiles from npx create-react-app redux-demo --template redux
but I’m clearly missing something.
Thanks!
AffinityModal.js
import React from 'react'; import { Button, Form, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader, Row, } from 'reactstrap'; import { affinOpen, toggleAffinAsync } from '../modalSlice' import { useDispatch } from 'react-redux'; function AffinityModal() { const dispatch = useDispatch(); return ( <Modal isOpen={affinOpen} toggle={() => dispatch(toggleAffinAsync())}> <ModalHeader> <h5 className="modal-title" id="exampleModalLabel">New Ingredient Affinity</h5> <Button data-dismiss="modal" aria-label="Close" className="close"> <span aria-hidden="true">×</span> </Button> </ModalHeader> <ModalBody> <div className="container-fluid"> <Form> <FormGroup> <Row> <div className="col-12"> <Label for="mainIngName" className="col-form-label">Main Ingredient:</Label> </div> </Row> <Row> <div className="col-12"> <Input readOnly type="text" id="mainIngName"></Input> </div> </Row> </FormGroup> <FormGroup> <Row> <div className="col-12"> <Label for="added-ing-text" className="col-form-label">Combines Well With:</Label> </div> </Row> <Row id="secondaryIngs"> <div className="col-10"> <Input type="text" id="added-ing-text" className="secIngInputs"></Input> </div> <div className="col-2"> <Button id="ingPlusButton">+</Button> </div> </Row> </FormGroup> </Form> </div> </ModalBody><ModalFooter> <Button data-dismiss="modal" onClick={() => dispatch(toggleAffinAsync())} color="secondary">Close</Button> <Button id="submitNewIngButton" color="primary" className="submitButton">Submit</Button> </ModalFooter> </Modal > )
}
export default AffinityModal
modalSlice.js
import { createSlice } from '@reduxjs/toolkit'; export const modalSlice = createSlice({ name: 'openAffinityModal', initialState: { isAffinityModalOpen: false, isRecipeModalOpen: false }, reducers: { toggleAffinityModal: state => { state.isAffinityModalOpen = !state.isAffinityModalOpen }, toggleRecipeModal: state => { state.isRecipeModalOpen = !state.isRecipeModalOpen } } }) export const { toggleAffinityModal, toggleRecipeModal } = modalSlice.actions; export const toggleAffinAsync = isAffinityModalOpen => dispatch => { dispatch(toggleAffinityModal); }; // The function below is called a selector and allows us to select a value from // the state. Selectors can also be defined inline where they're used instead of // in the slice file. For example: `useSelector((state) => state.counter.value)` //useSelector((state) => state.openAffinityModal.isAffinityModalOpen) export const affinOpen = state => state.openAffinityModal.isAffinityModalOpen; export default modalSlice.reducer;
Advertisement
Answer
You don’t need toggleAffinAsync
at all. Just use the regular action creator toggleAffinityModal
.
affinOpen
is a selector function. It is not a value. Right now your Modal
is always open because you are passing this function to the isOpen
prop and a function
is truthy when cast to a boolean
.
In order to get the boolean
value from the state, you need to call affinOpen
with useSelector
.
function AffinityModal() { const dispatch = useDispatch(); const isOpen = useSelector(affinOpen); return ( <Modal isOpen={isOpen} toggle={() => dispatch(toggleAffinityModal())}> ...