Неправильное понимание проблемы с порталом в React

#javascript #reactjs #redux

#javascript #reactjs #переопределение

Вопрос:

Создание простого приложения с использованием React и Redux.

Суть в том, чтобы получить фотографии с сервера и показать их. Если вы нажмете на фотографию, вы получите модальное окно с увеличенной фотографией и комментариями. Я использую Portal в своем модальном компоненте.

Форма кода ModalContainer

 import React from 'react'
import { closeModal } from '../redux/actions/actions'
import Modal from '../components/modal/Modal'
import axios from 'axios'
import { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useEffect } from 'react'

const ModalContainer = () => {

  const [user, setUser] = useState({
    name: '',
    comment: '',
  })
  const { photo, isOpen } = useSelector(({ modal }) => modal);
  const dispatch = useDispatch();

  useEffect(() => {
    if (isOpen) {
      document.body.style.overflow = 'hidden'
    }
    return () => {
      document.body.style.overflow = 'auto'
    }
  }, []);

  const handleChange = ({ target: { value, name } }) => {
    setUser({
      ...user,
      [name]: value
    })
  }

  const modalClose = () => {
    dispatch(closeModal())
  }

  const onOverlayClick = (e) => {
    dispatch(closeModal())
    e.stopPropagation()
  }

  const onModalClick = (e) => {
    e.stopPropagation()
  }

  const pushComment = (e) => {
    e.preventDefault()
    axios
      .post(
        `https://boiling-refuge-66454.herokuapp.com/images/${photo.id}/comments`,
        { name: user.name, comment: user.comment, date: Date.parse(String(new Date())) }
      )
      .then((res) => {
        console.log(res)
      })
      setUser({
        name: '',
        comment: '',
      })  
  }

  return (
    <Modal
      onClick={modalClose}
      onLayoutClick={onOverlayClick}
      onModalClick={onModalClick}
      src={photo.url}
      comments={photo.comments}
      name={user.name}
      comment={user.comment}
      onChange={handleChange}
      onSubmit={pushComment}
    />
  )
}

export default ModalContainer
  

Код для портала

 import ReactDOM from 'react-dom';
import { useEffect } from 'react';

const Portal = ({ children }) => {
  
  const el = document.createElement('div');

  useEffect(() => {
    document.body.appendChild(el);
    return () => {
      document.body.removeChild(el);
    } 
  }, [])

  return ReactDOM.createPortal(children, el);
}

export default Portal
  

Код для модального

 import React, { Fragment } from 'react'
import './Modal.scss'
import close from '../../images/close.png'
import Input from '../input/Input'
import Button from '../button/Button'
import PropTypes from 'prop-types'
import Portal from '../portal/Portal'

const Modal = ({
  onClick,
  onChange,
  onSubmit, 
  src,
  comments,
  name,
  comment,
  onModalClick,
  onLayoutClick,
}) => {
  return (
    <Portal>
      <div className="modal__layout" onClick={onLayoutClick}>
        <div className="modal__window" onClick={onModalClick}>
          <div className="modal__content">
            <div className="modal__item">
              <img src={src} alt={src} />
              <div className="modal__comments">
                <div className="modal__comment">
                  {comments.map((comment) => {
                    return (
                      <Fragment key={comment.id}>
                        <p>{new Date(comment.date).toLocaleDateString()}</p>
                        <p>{comment.text}</p>
                      </Fragment>
                    )
                  })}
                </div>
              </div>
            </div>
            <form onSubmit={onSubmit}>
              <Input
                name="name"
                type="text"
                value={name}
                placeholder="Ваше имя"
                onChange={onChange}
              />
              <Input
                name="comment"
                type="text"
                value={comment}
                placeholder="Ваш комментарий"
                onChange={onChange}
              />
              <Button>Оставить комментарий</Button>
            </form>
            <img className="close" src={close} alt="close" onClick={onClick} />
          </div>
        </div>
      </div>
    </Portal>
  )
}

Modal.propTypes = {
  onClick: PropTypes.func,
  onModalClick: PropTypes.func,
  src: PropTypes.string,
  comments: PropTypes.array,
  onChange: PropTypes.func,
  name: PropTypes.string,
  comment: PropTypes.string,
  onSubmit: PropTypes.func,
  onLayoutClick: PropTypes.func,
}

Modal.defaultProps = {
  onClick: () => {},
  onModalClick: () => {},
  src: '',
  comments: [],
  onChange: () => {},
  name: '',
  comment: '',
  onSubmit: () => {},
  onLayoutClick: () => {},
}

export default Modal
  

Проблема в том, что когда я открываю модальное окно с увеличенной фотографией и комментариями и пытаюсь заполнить входные данные или нажать кнопку без текста во входных данных, чтобы отправить комментарий на сервер, модальное окно закрывается и вообще не реагирует. Но без компонента портала он работает отлично. В чем проблема?

Комментарии:

1. Что вы имеете в виду, говоря, что вообще не реагирует?

2. Во-первых, это плохая практика игры с DOM в приложении react

3. @simbathesailor, я имею в виду, что вы не можете открыть модальное окно

4. @Rajesh, какой-нибудь совет тогда?

Ответ №1:

Вы делаете что-то вроде этого. Дайте мне знать, если это приведет к улучшению результатов.

 // Code form ModalContainer 

<Modal
   
      onClick={modalClose}
      onLayoutClick={onOverlayClick}
      onModalClick={onModalClick}
      src={photo.url}
      comments={photo.comments}
      name={user.name}
      comment={user.comment}
      onChange={handleChange}
      onSubmit={pushComment}
   isOpen={isOpen}
    />

// Modal code
const Modal = ({
  onClick,
  onChange,
  onSubmit, 
  src,
  comments,
  name,
  comment,
  onModalClick,
  onLayoutClick,
  isOpen
}) => {
  return (
    <Portal isOpen={isOpen}>
      <div className="modal__layout" onClick={onLayoutClick}>
        <div className="modal__window" onClick={onModalClick}>
          <div className="modal__content">
            <div className="modal__item">
              <img src={src} alt={src} />
              <div className="modal__comments">
                <div className="modal__comment">
                  {comments.map((comment) => {
                    return (
                      <Fragment key={comment.id}>
                        <p>{new Date(comment.date).toLocaleDateString()}</p>
                        <p>{comment.text}</p>
                      </Fragment>
                    )
                  })}
                </div>
              </div>
            </div>
            <form onSubmit={onSubmit}>
              <Input
                name="name"
                type="text"
                value={name}
                placeholder="Ваше имя"
                onChange={onChange}
              />
              <Input
                name="comment"
                type="text"
                value={comment}
                placeholder="Ваш комментарий"
                onChange={onChange}
              />
              <Button>Оставить комментарий</Button>
            </form>
            <img className="close" src={close} alt="close" onClick={onClick} />
          </div>
        </div>
      </div>
    </Portal>
  )
}

Modal.propTypes = {
  onClick: PropTypes.func,
  onModalClick: PropTypes.func,
  src: PropTypes.string,
  comments: PropTypes.array,
  onChange: PropTypes.func,
  name: PropTypes.string,
  comment: PropTypes.string,
  onSubmit: PropTypes.func,
  onLayoutClick: PropTypes.func,
}


// Portal Code



import ReactDOM from 'react-dom';
import { useEffect, useState } from 'react';

const Portal = ({ children, isOpen }) => {
  
  const [elem, setElem] = useState(null)
 
  

  useEffect(() => {
    const el = document.createElement('div');
    document.body.appendChild(el); 
    setElem(el)
    return () => {
      document.body.removeChild(el);
    }
  }, [])
  // useEffect(() => {
  //   if(isOpen) {
  //      document.body.appendChild(el); 
  //   }
  //  return () => {
  //     document.body.removeChild(el);
  //   } 
  // }, [isOpen])
  

  if(!el || !isOpen) return null
  if(el amp;amp; isOpen) return  ReactDOM.createPortal(children, el);

}

export default Portal