Skip to content

My hooks aren’t opening and closing my modals

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;

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())}>
...

Code Sandbox Demo