reactjsreact-nativemodal-dialog

Best practice for managing multiple modals in React Native


I have a screen and I need to show multiple modals in it. For example, if some request was failed then I want to show an error modal, if some request was successful then I want to show a success modal.

I currently do it as following which I think is bad practice:

...

export default function SomeSampleScreen(props) {
  const [errorModalVisible, setErrorModalVisible] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');
  const [successModalVisible, setSuccessModalVisible] = useState(false);
  const [successMessage, setSuccessMessage] = useState('');

  function showError(message) {
    setErrorMessage(message);
    setErrorModalVisible(true);
  }

  function showSuccess(message) {
    setSuccessMessage(message);
    setSuccessModalVisible(true);
  }

  return (
    <>
      <ErrorModal
        visible={errorModalVisible}
        onClose={() => {
          setErrorModalVisible(false);
        }}>
        {errorMessage}
      </ErrorModal>
      <SuccessModal
        visible={successModalVisible}
        onClose={() => {
          setSuccessModalVisible(false);
        }}>
        {successMessage}
      </SuccessModal>
      <View>
        ...
      </View>
    </>
  );
}

Solution

  • You could just condense it into one object:

    export default function SomeSampleScreen(props) {
      const [modalState, setModalState] = useState({state: ''});
    
      function showError(message) {
        setModalState({state: "error", message});
      }
    
      function showSuccess(message) {
        setModalState({state: "success", message});
      }
    
      return (
        <>
          <ErrorModal
            visible={modalState.state === "error"}
            onClose={() => {
              setModalState({state: ''});
            }}>
            {modalState.message}
          </ErrorModal>
          <SuccessModal
            visible={modalState.state === "success"}
            onClose={() => {
              setModalState(false);
            }}>
            {modalState.message}
          </SuccessModal>
          <View>
            ...
          </View>
        </>
      );
    }
    

    UDPATE After clarifying what the actual question is, here is a good way to do it. Create a Context:

    const ModalContext = React.createContext({status: "", message: ""});
    

    Add the context and the modals somewhere up in your tree:

    constructor(props) {
        super(props);
    
        this.setModal = (modalState) => {
          this.setState(state => ({...state, ...modalState}));
        };
        this.state = {
          status: "",
          message: "",
          setModal: this.setModal,
        };
      }
    return <ModalContext.Provider value={this.state.modalState}>
            <RestOfApp />
            <Modals/>
          </ModalContext.Provider>
    

    The Modals would be similar to what you postet:

    export default function SomeSampleScreen(props) {
      const modalState = useContext(ModalContext);
    
      function showError(message) {
        modalState.setModal({state: "error", message});
      }
    
      function showSuccess(message) {
        modalState.setModal({state: "success", message});
      }
    
      return (
        <>
          <ErrorModal
            visible={modalState.state === "error"}
            onClose={() => {
              modalState.setModal({state: ''});
            }}>
            {modalState.message}
          </ErrorModal>
          <SuccessModal
            visible={modalState.state === "success"}
            onClose={() => {
              modalState.setModal(false);
            }}>
            {modalState.message}
          </SuccessModal>
          <View>
            ...
          </View>
        </>
      );
    }