Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Portales: teletransportación de componentes

18/23
Recursos

Aportes 51

Preguntas 19

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Damas y Caballeros, bienvenidos a la clase de Portals

Primeramente, para que el botón se mantuviera visible lo que hice fue agregarle un z-index en el css del componente CreateTodoButton de la siguiente forma:

.CreateTodoButton {
  z-index: 1;
}

Y luego para poder cerrarlo lo que hice fue enviarle ademas de setOpenModal el atributo openModal al componente CreateTodoButton para poder crear un condicional

<CreateTodoButton setOpenModal={setOpenModal} openModal={openModal} />
function CreateTodoButton({ setOpenModal, openModal }) {
  const onClickButton = () => {
    if (openModal) {
      setOpenModal(false);
    } else {
      setOpenModal(true);
    }
  };

  return (
    <button className="CreateTodoButton" onClick={() => onClickButton()}>
      +
    </button>
  );
}

para tener en cuenta en react 18

para hacer render del root se usa

import ReactDOM from 'react-dom/client';

pero para hacer render del modal se usa el anterior

import ReactDOM from "react-dom";

espero les sirva el aporte 😃

Me da risa la emoción de Juan. Se emociona cuando aparece el modal y luego nos manda el reto 😂 😂 😂.
¡A por el reto!

Al igual que los compañeros use el useContext para practicar su uso y en el css agregué el z-index:

function CreateTodoButton() {
  const { openModal, setOpenModal } = React.useContext(TodoContext);

  const onClickButton = () => {
    setOpenModal(!openModal);
  };

  return (
    <button className="CreateTodoButton" onClick={onClickButton}>
      +
    </button>
  );
}
.CreateTodoButton{
	z-index: 1;
}

Los portales en React sirven para teletransportar componentes a un nodo de HTML distinto al nodo principal de la aplicación.

Para los que hagan el curso con React 18, si les sale en el navegador el siguiente error ->
"“react_dom_client__WEBPACK_IMPORTED_MODULE_1__.createPortal is not a function”",
si en su archivo modal.js importaron ReactDOM de 'react-dom/cliente, lo cambian por ’ react-dom ’

Aqui estan los estilos CSS

.ModalBackground {
  background: rgba(32, 35, 41, .8);
  position: fixed;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  display: flex;
  justify-content: center;
  align-items: center;
  color: white;
}

Buenas, yo quise seguir manteniendo la técnica del useContext ya que estamos usando el Provider para enviar las props y state y me esta convenciendo de que es la forma más limpia de mantener controlado y de fácil mantención el pasaje de props y estados.

function CreateTodoButton(){
    const {
        setOpenModal,
        openModal,
    } = React.useContext(TodoContext);

    const handleClick = () => {
        setOpenModal(!openModal);
    }

    return (
        <button  
                className="CreateTodoButton" 
                onClick={handleClick}
        >
            +
        </button>
    );
}

Luego, para mantener por arriba al createTodoButton con la propiedad css z-index tanto en css del Modal y del CreateTodoButton se soluciona sencillamente.

Hola, yo use el react hook useContext

CSS:
z-index: 1;

JS Toggle:
    const { openModal, setOpenModal } = useContext(TodoContext)

    const onClickButton = ()=>{
        !openModal ? setOpenModal(true) : setOpenModal(false)
    }

El caso de uso que se me ocurre para los portales es, teniendo un sitio web estático, de puro HTML, CSS, poder usar React no en una, sino en algunas partes (el root y los portales), y conservar gran parte del HTML original, y sin tener que reconstruir todo el sitio con React.

Si vienes de Vue, esta herramienta es similar a los Slots.

Esta es mi solución para el reto para cerrar el modal

en el css solo use un z-index: 999; para que el boton esté sobre cualquier otro elemento

En JS hice los siguiente

import React, { useContext } from 'react';
import { TodoContext } from '../../Context/TodoContext';
import './CreateTodoButton.css';

export const CreateTodoButton = () => {
  const { openModal, setOpenModal } = useContext(TodoContext);

  const handleClick = () => {
    setOpenModal(!openModal);
  };

  return (
    <>
      <button
        className="CreateTodoButton"
        type="submit"
        onClick={() => handleClick()}
      >
        {openModal ? 'x' : '+'}
      </button>
    </>
  );
};

Portales


¿Qué son?

Una API para renderizar componentes fuera de la jerarquía DOM de su aplicación.
Incluso podrías renderizar cosas en una ventana nueva! 😎
.

¿Cómo se crean?


.

¿Para qué?

Perfecto para ocasiones donde los estilos CSS restringen los elementos. Por ejemplo, problemas de apilamiento (z-index) y desbordamiento (overflow).
.

¿Cómo usarlos?

En lugar de retornar un elemento en el método render de un componente, retorna un portal.

El componente <Afuera/> se renderizará como descendiente directo de document.body 👍🏻
.

¿Cuándo usarlos?

  • Modales
  • Tooltips
  • Menús flotantes
  • Widgets
import React from "react";
import './CreateTodoButton.css'

function CreateTodoButton({setOpenModal, openModal}){
    const onClickButton = () => {
        setOpenModal(!openModal)
    }

    return(
        <button className="CreateTodoButton"
        onClick={onClickButton}>+</button>
    )
}

export { CreateTodoButton }
.CreateTodoButton {
	z-index: 1;
}

Solución al reto

Modal

import React from "react";
import "./createTodoButton.css";
const CreateTodoButton = ({ setOpenModal, openModal }) => {
  const onClick = () => setOpenModal(!openModal);
  return (
    <button
      className="create-todo-button"
      type="button"
      onClick={onClick}
    >
      +
    </button>
  );
};

export default CreateTodoButton;

css boton de crear

.create-todo-button {
  background-color: #61dafa;
  box-shadow: 0px 5px 25px rgba(97, 218, 250, 0.5);
  border: none;
  border-radius: 50%;
  cursor: pointer;
  font-size: 50px;
  position: fixed;
  bottom: 24px;
  right: 24px;
  font-weight: bold;
  color: #fafafa;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 64px;
  width: 64px;

  transform: rotate(0);
  transition: 0.3s ease;
  z-index: 999;
}

Componente Boton

import React from "react";
import "./createTodoButton.css";
const CreateTodoButton = ({ setOpenModal, openModal }) => {
  const onClick = () => setOpenModal(!openModal);
  return (
    <button
      className="create-todo-button"
      type="button"
      onClick={onClick}
    >
      +
    </button>
  );
};

export default CreateTodoButton;

Para el reto de cerrar el modal simplemente pase el estado actual osea openModal a buttom y lo pase por parametro a su actualizador setOpenModal y lo niegue para que me devolviera el valor contrario.

function CreateTaskButton(props) {
  const onClickButton = () => {
    props.setOpenModal(!props.openModal);
  }
  return (
    <button
      className="CreateTaskButton"
      onClick={onClickButton}
    >
      +
    </button>
  );
}

export { CreateTaskButton };```

Aquí mi sencilla solución:

en AppIU.js lo primero que hice fue pasarle openModal también

<CreateTodoButton 
        setOpenModal={setOpenModal}
        openModal={openModal}
      />

luego en CreateTodoButton

function CreateTodoButton({ openModal , setOpenModal }) {

  const onClickCreateTodoBotton = () => {
    setOpenModal(!openModal);
  };

  return (
    <button 
      className="CreateTodoButton"
      onClick={onClickCreateTodoBotton}
    >
    +
    </button>
    );
}

Mi solucion al reto fue, en CreateTodoButton.css, agregue un z-index: 1 y para hacer que el modal se abra o cierre le pase al componente la propiedad openModal y agregue lo siguiente en CreateTodoButton

const onClickButton = (msg) => {
    props.setOpenModal(!props.openModal);
 };

Lo que hice fue darle a button un z-index de 1, después pararle las propiedades setOpenModal={setOpenModal} openModal={openModal} a el componente <CreateTodoButton /> y en su index.js decirle que en el evento onclick utilizara props.setOpenModal(!props.openModal) para que vaya alternando entre true o false dependiendo del click.

Juan Dc Ama lo que hace, sin duda.

Esta fue mi solución:

function CreateTodoButton (){
    const {setOpenModal, openModal} = React.useContext(TodoContext);
    const buttonFunction = () => {
        if (openModal){
            setOpenModal(false)
        } else{
            setOpenModal(true)
        }
    };

    return (
        <button className='button' onClick={()=> buttonFunction()}>
            Add Task
        </button>
    )
}

juan: es hora de terminar nuestra aplicacion 😃 😃 😃 😃
yo: esta toda rota mi app juan no mms xd

Para importar las props use useContext y arrow function para simplificar onClick sin perder legibilidad

const { openModal, setOpenModal } = React.useContext(TodoContext);
const onClickButton = () => setOpenModal(!openModal)

Solo agregar z-index: 1;

a las clases del boton, y listo

Reto completado 🌭

CreateTodoButton --> index.js

function CreateTodoButton(props) {

  const onClickButton = () => {
    props.openModal ? props.setOpenModal(false) : props.setOpenModal(true) 
  }

  return (
    <button className="CreateTodoButton"
      onClick={onClickButton} 
    >
      +
    </button>
  );
}

CreateTodoButton --> css, solo le agrego

  z-index: 1;

Practica

CreateTodoButton.js

import React from 'react';
import './CreateTodoButton.css';
import {TodoContext} from "../TodoContext";

function CreateTodoButton() {
    const {
        openModal,
        setOpenModal,
    } = React.useContext(TodoContext);
    const onClickButton = () => {
        (!openModal)
            ? setOpenModal(true)
            : setOpenModal(false)
    }
    return (
        <button
            className={ "CreateTodoButton "+ (openModal ? 'active' : '' )}
            onClick={onClickButton}
        >+</button>
    )
}

export {CreateTodoButton};

CreateTodoButton.css

.CreateTodoButton {
  background-color: #61DAFA;
  box-shadow: 0px 5px 25px rgba(97, 218, 250, 0.5);
  border: none;
  border-radius: 50%;
  cursor: pointer;
  font-size: 50px;
  position: fixed;
  bottom: 24px;
  right: 24px;
  font-weight: bold;
  color: #FAFAFA;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 64px;
  width: 64px;

  transform: rotate(0);
  transition: .3s ease;
  z-index: 1;
}

.CreateTodoButton:hover, .CreateTodoButton.active {
  transform: rotate(224deg);
}

Hola, para hacer que el signo de más se vea como una tachita cuando se abra el modal, tienes que agregar este CSS al final del archivo CreateTodoButton.css

.CloseTodoButton {
	transform: rotate(45deg);
}

Ahora solo basta con agregar esta clase cuando el modal esté abierto, supongo que hay varias maneras de hacerlo pero yo lo hice así (Archivo CreateTodoButton/index.js)

import React from 'react';
import './CreateTodoButton.css';
import { TodoContext } from '../TodoContext';

function CreateTodoButton(props) {
	const { openModal, setOpenModal } = React.useContext(TodoContext);

	let classNameButton = "CreateTodoButton";
	classNameButton += openModal ? " CloseTodoButton": "";
  return (
		<button
			className={classNameButton}
			onClick={() => setOpenModal(!openModal)}
		>
			+
		</button>
  );
}

export { CreateTodoButton };

Ahí mismo podrás encontrar como abrir y cerrar el modal de la manera más sencilla posible.

Mi solución al reto del toggle sería así:
1- Cambiar la propiedad z-index de la clase “CreateTodoButton” en el archivo .css

  z-index: 10; /*o cualquier valor superior al del <div id="modal"></div>

2- Pasar “openModal” como props al componente “CreateTodoButton” en el archivo de AppUI.js

      <CreateTodoButton 
        setOpenModal={setOpenModal} 
        openModal={openModal}
      />

3- Recibir la propiedad “openModal” en la definición del componente “CreateTodoButton” y asignar la negación del valor de “openModal” al momento de llamar a la función “setOpenModal”

const CreateTodoButton = ({ openModal, setOpenModal }) => {
  
  const onClickButtom = () => {
    setOpenModal(!openModal)
  }
  ...

Los React Portals te permiten comunicar el nodo principal donde se renderiza nuestra aplicación con otro nodo generado en HTML. Ambos se pueden comunicar entre sí recibiendo cambios en el estado y en las props.

Personalmente prefiero pasarle un componente como prop a los modales. Eg:

import React from "react";
import ReactDOM from "react-dom";

function Modal({ component }) {
	return ReactDOM.createPortal(
		<>{component}</>,
		document.getElementById('modal')
	)
}

export { Modal }

Y usarlo de la siguiente forma:

<Modal component={ <UnFormConMuchosInputs /> } />

A mi gusto queda más legible y reutilizable 😄

Dentro de AppUI.js agregue el estado del modal,

      <CreateTodoButton 
        openModal={openModal}
        setOpenModal={setOpenModal}
      />

para recibirlo en props dentro de CreateTodoButton/index.js

    const onClickButton = () => {
        props.setOpenModal(!props.openModal)
    }

donde cambio su valor al contrario del que tiene permitiendo el toogle

Lo que yo hice fue: En AppUI.js agregue esta otra propiedad:
<CreateTodoButton
setOpenModal={setOpenModal}
openModal={openModal}
/>

Luego en el index.js del createButton agregue estas líneas,
const onClickButton =()=> {
props.openModal ? props.setOpenModal(false) : props.setOpenModal(true);
}
Para que alterne entre el true y el false,
Y para que el botón se viera, fui al css del mismo y le agregué un z-index: 10;
Para que se superponga a todo el contenido.

Me pareció mejor cerrar el modal cuando el usuario haga click en cualquier parte de la pantalla menos del contenido del modal.

import { TodoContext } from 'context/todoContext';
import { useContext } from 'react';
import ReactDOM from 'react-dom'
import './Modal.css'

function Modal({ children }) {
  const { openModal, setOpenModal } = useContext(TodoContext)
  return ReactDOM.createPortal(
    openModal &&
    <div className="modal-wrapper" onClick={()=> setOpenModal(false)}>
      <div className="modal-content" onClick={(event)=> event.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.getElementById('modal') as Element
  );
}

export default Modal;
/*Modal.css*/
.modal-wrapper{
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.8);
  display: flex;
  justify-content: center;
  align-items: center;
}
.modal-content{
  width: 100%;
  max-width: 500px;
  background: #fff;
  border-radius: 5px;
  padding: 20px;
  box-shadow: 0 0 10px rgba(0,0,0,0.5);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

Para que no se cierre el modal al hacer click en el contenido, evitamos la propagación del evento al padre, con el codigo event.stopPropagation()

MI solución a los retos, con el button como todo buen programador z-index: 999; XD

Y al Modal

function CreateTodoButton({ openModal, setOpenModal }) {
  const onClickButton = () => {
    !openModal ? setOpenModal(true) : setOpenModal(false);
  };
}

Estuvo genial la clase:DDD

Usando useContext
/CreateTodoButton/CreateTodoButton.jsx

import React from 'react'
import "./CreateTodoButton.css"
import { TodoContext} from './../context/todoContext'
function CreateTodoButton() {
    const { openModal,setOpenModal}=React.useContext(TodoContext)
    const onClickButton = () => {
        setOpenModal(true)     
    }
    return (       
         <button onClick={()=>onClickButton()} className='CreateTodoButton'>+</button>      
    )
}
export   {CreateTodoButton}
Modal/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css'
 import { TodoContext} from './../App/context/todoContext'
const Modal = ({ children }) => {
  const { openModal,setOpenModal}=React.useContext(TodoContext)
    const onClickButton = () => {
        setOpenModal(false) 
    }
  return ReactDOM.createPortal( 
    <div className='modal'>
       <div className="modal-content">        
        <button className="close" onClick={()=>onClickButton()}>&times;</button>
  {children}
  </div></div> ,
  document.getElementById('modal')
);
 
}
export {Modal}

Modal/index.css

.modal {   
  position: fixed;  
  z-index: 1;     
  width: 100%; 
  height: 100%; 
  overflow: auto;     
  background-color: rgba(0,0,0,0.4);    
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  display: flex;
  justify-content: center;
  align-items: center; 
}

.modal-content {
  background-color: #fefefe;
  margin: 15% auto;  
  padding: 20px;
  border: 1px solid #888;
  width: 80%;  
}

.close {
  color: #aaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: black;
  text-decoration: none;
  cursor: pointer;
}

Con CSS para el button utilizé z-index: 2 y en el modal z-index: 1
Para abrir / cerrar el modal utilize el useContext:

const { openModal } = React.useContext(TaskContext);

const onClickButton = () => 
{
	props.setOpenModal( !openModal );
}; 

Este curso es una joya, me encanta todo lo que he aprendido

Mi solución fue abusar del poderoso useContext

import React from 'react';
import { TodoContext } from '../TodoContext';
import './CreateTodoButton.css'

function CreateTodoButton() {
    const { openModal, setOpenModal} = React.useContext(TodoContext);
    const onClickButton = () =>{
        setOpenModal(!openModal);
    }

    return (
        <button 
        className="CreateTodoButton"
        onClick={onClickButton}
        >+</button>
    )
}

export { CreateTodoButton }

Mi solución fue pasar el estado de openModal y usar un ternario.

function CreateTodoButton({ openModal, setOpenModal }) {
  const onClickButton = () => {
    (openModal)
    ? setOpenModal(false)
    : setOpenModal(true)
  };
  . . .
}

Para el css solo use el z-index

.CreateTodoButton {
  . . .
  z-index: 2;
}

Mi solución al reto:

function CreateTodoButton({ setOpenModal }) {
  const [isActive, setIsActive] = useState(false);

  const handleClick = () => {
    setOpenModal((openModal) => !openModal);

    setIsActive(!isActive);
  };

  return (
    <button
      className={`CreateTodoButton ${isActive && "OpenModal"}`}
      onClick={handleClick}
    >
      +
    </button>
  );
}

isActive se encarga de manejar el estado de si el modal está abierto o no, si lo está, agrega una clase OpenModal al button.

En CreateTodoButton.css agregue una clase la cual, lo único que hace, es darle un z-index de 1.

.OpenModal {
  z-index: 1;
}

Mi solucion

function Modal({children, setOpenModal}) {
//la funcion handleClick cierra el modal
    function handleClick() {
        setOpenModal(false)
    }
    
    return ReactDom.createPortal(
        <div className="ModalBackground">
            {children} 
            <span onClick={handleClick}>
                X
            </span>
        </div>,
       
        document.getElementById('modal')
    );
}

Una posible solución para cerrar el Modal, puede ser la siguiente: Al hacer click en cualquier lugar afuera del componente “Portaleado”, se cierra la modal.
.
Al componente padre (Modal) se le añade el evento de al hacer click, cerrar modal.
.
Modal/index.js

export const Modal = ({ children, setOpenModal }) => {
  const handleClick = () => setOpenModal(false);

  return ReactDOM.createPortal(
    <Wrapper onClick={handleClick}>{children}</Wrapper>,
    document.getElementById('modal')
  );
};

Para impedir que al hacer click dentro del componente “portaleado” se cierre, se usa un evento stopPropagation()
.
AppUI.js

<Modal setOpenModal={setOpenModal}>
  <div onClick={(e) => e.stopPropagation()}>Transporteee</div>
</Modal>

Mi solución:

setOpenModal(!openModal);

Yo lo hice de esta manera, para cerrar el modal puse el ícono “X”. Dentro del evento onClick mando a llamar la siguiente función :

const { setOpenModal } = React.useContext(TodoContext);
const closeModalButton = () => {
      setOpenModal(false);
}

Y por lo pronto mi modal se ve de la siguiente manera:

Así esta bien para mí

Mi solucion al reto de abrir y cerrar el modal es de la siguiente manera:

export const CreateTodoButton = ({setOpenModal, openModal}) => {

    const handleClick = () => {
        setOpenModal(!openModal);
    }

    return (
        <button 
            className="btn-create"
            onClick={handleClick}
        ></button>
    );
}

coloque un z-index en el css del boton

z-index: 1;

y modifique el renombre el onClickButton para llamarlo onToggleButton y
en setOpenModal agregue la negación de su valor actual.

const onToggleButton = () => {
        props.setOpenModal(!props.openModal);
    }

El uso de modales en los portales es de uso comun

Pregunta de examen:
¿Cómo creamos un portal en React?