Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIkit's modals in React: integration

I'm working on this project where the frontend is in React with UIkit for the user interface. The integration between the parts looks poorly implemented. I'm going to explain why. There is a Modal component, something like

export class Modal extends Component {
  static getByName = name => UIkit.modal(`[data-modal-name='${name}']`)

  static show = name => {
    const modal = Modal.getByName(name)
    if (modal) modal.show()
  }

  static hide = name => {
    const modal = Modal.getByName(name)
    if (modal) modal.hide()
  }

  render() {
    // a modal
  }
}

this is used in this way

export const LoginFormModal = props => (
  <Modal name="login-form" className="login-form-modal" hideClose>
    <LoginForm />
  </Modal>
)

and show/hide is called programmatically where needed (even redux's actions)

Modal.hide("login-form")

this is in a Redux action, like this

export const login = credentials => {
  return dispatch => {
    dispatch(showLoader())

    API.authentication.login(
      credentials,
      response => {
        setCurrentUser(
          Object.assign({}, response.user, { user_id: response.user.id })
        )
        Modal.hide("login-form")
        dispatch(loginSucceded(response))
        dispatch(hideLoader())
        dispatch(push("/"))
        dispatch(fetchNotificationsCounter())
      },
      error => {
        dispatch(loginFailed(error))
        dispatch(hideLoader())
      }
    )
  }
}

This seems to work. Until you leave a component. When you come back to it, the second time the programmatically hide does not work anymore.

Anyone can lead me to how integrate the parts in a more react-appropriate way?

like image 968
Ursus Avatar asked Mar 27 '18 11:03

Ursus


People also ask

How do you add modals in react?

jsx import React from "react"; import styles from "./Modal. module. css"; import { RiCloseLine } from "react-icons/ri"; const Modal = ({ setIsOpen }) => { return <h1>Hello Modal</h1>; }; export default Modal; With all that done we can start working on our template.

What are modals in react?

A modal is a message box that is displayed on top of your screen. Modals put an overlay on the screen; therefore, they take visual precedence over all the other elements.

How show modal on button click react?

Add The State and Methods Add a button inside the render block to trigger the modal. When the button is clicked, the isOpen state will be set to true. Now, to display the modal, all you need to do is pass the isOpen state value to the show prop of the <Modal /> component.


2 Answers

Using the parts of uikit which manipulate the dom (show, hide) is obviously hard to connect with React (and probably you shouldn't), however:

You need to move the call of the functions show and hide inside the Component by passing the bool of the state of the modal (eg. modalopen) . A good hook is the componentWillReceiveProps which can be used to check the previus props

componentWillReceiveProps(nextProps) {
  if (nextProps.modalopen !== this.props.modalopen) {
    if (nextProps.modalopen) {
      getByName(...).show()
    } else {
      getByName(...).hide()
    }
  }
}

(this is inside the Modal class)

like image 186
MaanooAk Avatar answered Sep 28 '22 03:09

MaanooAk


The thing I don't like and that is definitely not a "React-way" is that the code is mutating state directly from an action creator (!). From React docs:

For example, instead of exposing open() and close() methods on a Dialog component, pass an isOpen prop to it.

So what if you had one modal that would be controlled by the redux state? Here is a possible implementation:

ModalWindow - will react to state changes and render depending what's in store:

import React from 'react';
import InfoContent from './InfoContent';
import YesOrNoContent from './YesOrNoContent';
import { MODAL_ACTION } from './modal/reducer';

class ModalWindow extends React.Component {
  renderModalTitle = () => {
    switch (this.props.modalAction) {
        case MODAL_ACTION.INFO:
          return 'Info';
        case MODAL_ACTION.YES_OR_NO:
          return 'Are you sure?';
        default:
          return '';
    }
  };

  renderModalContent = () => {
    switch (this.props.modalAction) {
      case MODAL_ACTION.INFO:
        return <InfoContent />;
      case MODAL_ACTION.YES_OR_NO:
        return <YesOrNoContent />;
      default:
        return null;
    }
  };

  render() {
    return (
        this.props.isModalVisible ?
        <div>
           <p>{this.renderTitle()}</p> 
           <div>
              {this.renderModalContent()}
           </div>
        </div>
        :
        null
    );
  }
}

export default connect((state) => ({
    modalAction: state.modal.modalAction,
    isModalVisible: state.modal.isModalVisible,
}))(ModalWindow);

modal reducer it will expose API to show/hide modal window in the application:

export const SHOW_MODAL = 'SHOW_MODAL';
export const HIDE_MODAL = 'HIDE_MODAL';

const INITIAL_STATE = {
  isModalVisible: false,
  modalAction: '',
};

export default function reducer(state = INITIAL_STATE, action) {
  switch (action.type) {
    case SHOW_MODAL:
      return { ...state, isModalVisible: true, modalAction: action.modalAction };
    case HIDE_MODAL:
      return { ...state, isModalVisible: false };
    default:
      return state;
  }
}

export const MODAL_ACTION = {
  YES_OR_NO: 'YES_OR_NO',
  INFO: 'INFO',
};

const showModal = (modalAction) => ({ type: SHOW_MODAL, modalAction });
export const hideModal = () => ({ type: HIDE_MODAL });
export const showInformation = () => showModal(MODAL_ACTION.INFO);
export const askForConfirmation = () => showModal(MODAL_ACTION.YES_OR_NO);

So basically you expose simple API in form of redux action-creators to control the state of your ModalWindow. Which you can later use like:

dispatch(showInformation())
...
dispatch(hideModal())

Of course, there could be more to it like optional configuration that would be passed to action creators or queue for modals.

like image 37
Tomasz Mularczyk Avatar answered Sep 28 '22 03:09

Tomasz Mularczyk