Mohsin Aboobacker
Mohsin Aboobacker

Reputation: 69

How to update parent component with a state that has updated within a custom hook?

I'm not sure if this has been asked before. I couldn't find anything after googling.

I have a parent component which basically has a button which when clicked will open a modal.

Inside the modal, there is a form which makes a post-call to an API. If the post-call is successful, I need the modal to be closed. I'm experimenting custom hooks to achieve this.

Below is my code:

Custom Hook

type savedHook = {
    saved: boolean,
    loading: boolean,
    error: string,
    saveSearch: (search: any) => void,
    showNewModal: boolean,
    setShowNewModal: (boolean) => void
};

export const useSaveSearch = () : savedSearchHook => {
    const [loading, setLoading] = useState(false);
    const [saved, setSaved] = useState(false);
    const [error, setError] = useState('');
    const [showNewSaveSearch, setNewShowSearch] = useState(false);

    const saveSearch = async (search: any) => {
        setLoading(true);

        fetch('my api', {
            method: 'POST',
            body: JSON.stringify(search),
            headers: {
                'Content-Type': 'application/json'
            }
        }).then((data) => {
            setSaved(true);
            setLoading(false);
            setShowNewModal(false);
        }).catch((error) => {
            setError(error);
            setLoading(false);
        });
    }

    const setShowNewModal = (show: boolean) => {
        setNewShowSearch(show);
    }


    return {
        error,
        loading,
        saveSearch,
        saved,
        setShowNewModal,
        showNewModal: showNewSaveSearch
    }

}

Modal

export default function SaveSearch({isOpen, onDismiss}) {
    const { state } = useSearch();
    const [name, setName] = useState('');
    const { loading, saved, error, saveSearch } = useSaveSearch();

    const handleSave = () => {
        saveSearch({
            name,
            query: state.query,
            type: state.type
        });
    }

    return (
      <Modal isOpen={isOpen} onDismiss={onDismiss}>
        <div>
          <span>Save Search ({state.type})</span>
          <IconButton styles={iconButtonStyles} iconProps={{iconName: 'Cancel'}} onClick={onDismiss} />
        </div>
        <div>
              <TextField label="Name" autoFocus value={name} onChange={(e, value) => setName(value)} />
                {loading && <Spinner size={SpinnerSize.small} />}
              <DefaultButton text="Save" onClick={handleSave} iconProps={{iconName: 'Save'}} disabled={name.length === 0 || loading} />


        </div>

      </Modal>
    )
  }

Parent Component

export default function ParentComponent() {
   const { showNewModal, setShowNewModal } = useSaveSearch();

   return (
     <div>
       {<SaveSearch isOpen={showNewModal} onDismiss={() => setShowNewModal(false)} />}
        <PrimaryButton text="Save Search" onClick={() => setShowNewModal(true)} iconProps={{iconName: 'Save'}} />
    </div>
   );
}

The problem I'm facing is, to open the modal, I'm calling setShowNewModal from the parent component which works fine. But after the save function, I'm calling setShowNewModal from the hook which doesn't get updated in the parent component.

Upvotes: 1

Views: 118

Answers (1)

Jolly
Jolly

Reputation: 1768

It would be good if you could provide a working example.

Anyways, if I'm correct, the setShowNewModal(false); inside the saveSearch method of useSaveSearch custom hook should close the Modal, right?
Well, if that's the case, inside setShowNewModal you just call setNewShowSearch. Then, the value of setNewShowSearch is returned for a property named showNewModal, but inside the Modal, when you write the following line:

const { loading, saved, error, saveSearch } = useSaveSearch();

You don't consider the showNewModal property, in the deconstructor. Maybe I'm missing something in the flow: that's why I was asking for a working demo.

Anyways, going back to the issue: inside the Modal component you could just pass the onDismiss method:

const { loading, saved, error, saveSearch } = useSaveSearch(onDismiss);

And inside the useSaveSearch custom hook, just call the onDismiss parameter, which will call the callback () => setShowNewModal(false) defined in the Parent component.

Upvotes: 1

Related Questions