sweetalert2 / sweetalert2-react-content

Official SweetAlert2 enhancer adding support for React elements as content
MIT License
695 stars 47 forks source link

swal does not rerender when the state is changed #162

Closed LorhanSohaky closed 3 years ago

LorhanSohaky commented 3 years ago

bug-sweetalert

https://codesandbox.io/s/bug-sweetalert-react-y763q

shubh27842 commented 3 years ago

Any update in this issue??? even i am facing same problem while updating quantity

mikedavies-dev commented 3 years ago

You need to wrap your content in a React component to get state working. Take a look at this closed issue:

https://github.com/sweetalert2/sweetalert2-react-content/issues/52

For example, instead of this:

ReactSwal.fire({
  html: (
    <input type="text" value={value} />
  )
 })

You would have something like this:


const Component = (props) => {
  const [value, setValue] = useState('');
  return (
    <input
      type="text"
      value={value}
      onChange={(e) => {
        setValue(e.target.value);
      }
    />
  );
}

ReactSwal.fire({
  html: (
    <Component />
  )
 })
shubh27842 commented 3 years ago
previous message > You need to wrap your content in a React component to get state working. Take a look at this closed issue: > > #52 > > For example, instead of this: > > ```js > ReactSwal.fire({ > html: ( > > ) > }) > ``` > > You would have something like this: > > ```js > const Component = (props) => { > const [value, setValue] = useState(''); > return ( > type="text" > value={value} > onChange={(e) => { > setValue(e.target.value); > } > /> > ); > } > > ReactSwal.fire({ > html: ( > > ) > }) > ```

thank you

LorhanSohaky commented 3 years ago

@ant-fx , but how to expose the state to outside the component?

For example:

const Component = (props) => {
  const [value, setValue] = useState('');
  return (
    <input
      type="text"
      value={value}
      onChange={(e) => {
        setValue(e.target.value);
      }
    />
  );
}
// ...
const openPopup = async () => {
  const { isConfirmed } = await ReactSwal.fire({
    html: (
      <Component />
  )

  if (isConfirmed) {
    await submitDate(popupData)
  }
 })
}
zenflow commented 3 years ago

how to expose the state to outside the component?

@LorhanSohaky Maybe there will be some better suggestion, but I have one:

const Component = (props) => {
  const [value, setValue] = useState('');
+ useEffect(() => { props.onValue(value) }, [value]);
  return (
    <input
      type="text"
      value={value}
      onChange={(e) => {
        setValue(e.target.value);
      }
    />
  );
}
// ...
const openPopup = async () => {
+ let popupData;
  const { isConfirmed } = await ReactSwal.fire({
    html: (
-      <Component />
+      <Component onValue={value => { popupData = value }} />
  )

  if (isConfirmed) {
    await submitDate(popupData)
  }
 })
}
zenflow commented 3 years ago

@LorhanSohaky Do you have a comment about my suggested solution? Or just the 👀 emoji? Does it not work?

zenflow commented 3 years ago

Closing this because

  1. The original stated issue ("swal does not rerender when the state is changed") is invalid. You have to encapsulate state in a component, as @ant-fx shared here https://github.com/sweetalert2/sweetalert2-react-content/issues/162#issuecomment-846960762 .. thanks @ant-fx!
  2. The follow up issue (about getting that data out of component state when the popup is closed) has a suggested solution here https://github.com/sweetalert2/sweetalert2-react-content/issues/162#issuecomment-849915971

@LorhanSohaky You're welcome

meotimdihia commented 3 years ago

This seems complicated, do we have easy solutions? if the state isn't a simple value. For example:

export default function SettingsForm({ readerOptions, setReaderOptions }) {

I need to change state by call setReaderOptions but HTML was rendered Swal don't update it.

LorhanSohaky commented 3 years ago

@LorhanSohaky Do you have a comment about my suggested solution? Or just the eyes emoji? Does it not work?

Its work for me, tks

ramilamparo commented 2 years ago

I am late to the party but I hope this can help someone. You can use React Portal to render items inside the modal content.

Sandbox Link.

import Swal from "sweetalert2";
import { createPortal } from "react-dom";
import withReactContent from "sweetalert2-react-content";
import { useEffect, useState, useCallback } from "react";

function App() {
  const [shown, setShown] = useState(false);
  const [value, setValue] = useState("");

  const handleClick = useCallback(function handleClick() {
    setShown(true);
  }, []);

  const handleClose = useCallback(function handleClose() {
    setShown(false);
  }, []);

  const handleChange = useCallback(function handleChange(e) {
    setValue(e.target.value);
  }, []);

  return (
    <div>
      <h1>{value}</h1>
      <button onClick={handleClick}>Alert</button>
      <SweetAlert2 show={shown} onClose={handleClose}>
        <input onChange={handleChange} value={value} />
      </SweetAlert2>
    </div>
  );
}

const MySwal = withReactContent(Swal);

const SweetAlert2 = ({ children, show, onClose }) => {
  const [container, setContainer] = useState(null);

  useEffect(() => {
    if (show) {
      MySwal.fire({
        html: <div ref={(el) => setContainer(el)} />
      }).then(() => {
        onClose && onClose();
      });
    }
  }, [show, onClose]);

  if (container) {
    return createPortal(children, container);
  }
  return null;
};
LorhanSohaky commented 2 years ago

@ramilamparo Did not know this function, very cool!

shaiozfize commented 1 year ago

I am late to the party but I hope this can help someone. You can use React Portal to render items inside the modal content.

Sandbox Link.

import Swal from "sweetalert2";
import { createPortal } from "react-dom";
import withReactContent from "sweetalert2-react-content";
import { useEffect, useState, useCallback } from "react";

function App() {
  const [shown, setShown] = useState(false);
  const [value, setValue] = useState("");

  const handleClick = useCallback(function handleClick() {
    setShown(true);
  }, []);

  const handleClose = useCallback(function handleClose() {
    setShown(false);
  }, []);

  const handleChange = useCallback(function handleChange(e) {
    setValue(e.target.value);
  }, []);

  return (
    <div>
      <h1>{value}</h1>
      <button onClick={handleClick}>Alert</button>
      <SweetAlert2 show={shown} onClose={handleClose}>
        <input onChange={handleChange} value={value} />
      </SweetAlert2>
    </div>
  );
}

const MySwal = withReactContent(Swal);

const SweetAlert2 = ({ children, show, onClose }) => {
  const [container, setContainer] = useState(null);

  useEffect(() => {
    if (show) {
      MySwal.fire({
        html: <div ref={(el) => setContainer(el)} />
      }).then(() => {
        onClose && onClose();
      });
    }
  }, [show, onClose]);

  if (container) {
    return createPortal(children, container);
  }
  return null;
};

@ramilamparo Nice! but using this showLoaderOnConfirm stopped working for me... (even though preConfirm does work!) does this reproduce for you also? any advice???