why destroyOnClose={true} not working in React
Asked Answered
R

3

8

I am developing a React hook based functional application with TypeScript and I am using modal from ant design. I'm submitting a form through modal for a table. So, the modal will be called for more than once to fill-up different rows of the table.

The problem is, when the modal is popping up for the second, third or lateral times, it's always carrying the previous values.

To avoid that, I set in the modal EnableViewState="false" , it didn't work . I set destroyOnClose={true}. It didn't work. In the modal documentation, it is written when destroyOnClose doesn't work then we need to use . But where to define it ? Because, when I am setting up as, <Form onSubmit={props.inputSubmit} preserve={false} in my modal form, I'm getting an error saying Type '{ children: Element[]; onSubmit: any; preserve: boolean; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Form>......?

what do you use so that every time the modal reloads, it reloads as empty ? I don't want to assign the state in the form value fields of the input. Is there any other option such as, destroyOnClose={true} ?

Here is my modal,

<Form onSubmit={props.inputSubmit}>
  <Row>
    <Col span={10}>
      <Form.Item>
        <Text strong={true}>Article name: </Text>
      </Form.Item>
    </Col>
    <Col span={12}>
      <Form.Item>
        <Input
          style={{ backgroundColor: '#e6f9ff' }}
          name="articleName"
          onChange={props.handleArticleModalInput}
        />
      </Form.Item>
    </Col>
  </Row>
</Form>

Here is from where the modal is getting called,

return (
  <>
    <ArticleTableModal
      destroyOnClose={true}
      isVisible={modalVisibilty}
      inputSubmit={inputSubmit}
      handleCancel={handleCancel}
      filledData={fetchedData}
      articleNumber={articleNumber}
      handleArticleModalInput={handleArticleModalInput}

    />

    <Table
      pagination={false}
      dataSource={articleDataSource}
      columns={articleColumns}
      scroll={{ y: 400 }}
      bordered
    />
  </>
)

Any help is much appreciated.

Rhineland answered 7/9, 2020 at 12:13 Comment(3)
Typically, one does not create a brand new modal dialog window every time. That may be why you're encountering friction. Recommend keep modal contents as state in a parent class component or useState hook (or custom hook) and updating that state when the modal is set. You can even have unmount logic to reset the contents to an empty string or some other non-value.Sulk
Hello @JaredSmith... thanks for trying to help me. But would it be possible for you to give example ? I am keeping "modal contents as state in a parent class component whenever use entering data", I am just not updating that value back to modal again. I thought that back and forth communication would make my application heavy. What do you mean by specially, "You can even have unmount logic to reset the contents to an empty string or some other non-value" ?Rhineland
See my answer below.Sulk
W
3

Form state is maintained by useForm outside the Modal.
"destroyOnClose" is the API in Modal, so it will only deal with the logic related to Modal, not the Form.
Above antd version 4.5, you can simply use "preserve" props. For Example:

<Modal destroyOnClose>
    <Form preserve={false} />
</Modal />
Whimsical answered 12/5, 2023 at 9:8 Comment(0)
E
1

You need to generate dynamic keys for the fields in the form on each modal launch.

Here's a sandbox to play around. If you don't make any changes to the key, the modal retains values inside it. If you change key and launch modal, the value gets cleared.

Sandbox Link

import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Modal, Button, Input } from "antd";

class App extends React.Component {
  state = { visible: false, theKey: "dummy" };

  showModal = () => {
    this.setState({
      visible: true
    });
  };

  handleOk = (e) => {
    console.log(e);
    this.setState({
      visible: false
    });
  };

  handleCancel = (e) => {
    console.log(e);
    this.setState({
      visible: false
    });
  };

  handleChange = ({ target: { value } }) => {
    this.setState({ theKey: value });
  };

  render() {
    return (
      <>
        <Input onChange={this.handleChange} placeholder="key for input field"/>
        <br />
        <Button type="primary" onClick={this.showModal}>
          Open Modal
        </Button>
        <Modal
          title="Basic Modal"
          visible={this.state.visible}
          onOk={this.handleOk}
          onCancel={this.handleCancel}
        >
          <Input
            key={this.state.theKey}
            style={{ backgroundColor: "#e6f9ff" }}
            name="articleName"
          />
        </Modal>
      </>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("container"));
Empirin answered 7/9, 2020 at 13:6 Comment(2)
Hello @Empirin ..... the solution of using dynamic key and changing that with state handling really helped . I will post a reply for this. For me emptying the passed states didn't help but this key handling worked like charm. I used the key for even a custom modal and called it from my other component ...thanks much !Rhineland
But with this implementation isn't there a chance of memory leak ?Rhineland
S
0

Here we'll use a custom hook that wraps a ModalDialog component from somewhere else (like a 3rd party UI library) and gives us back a tuple of a setter and a self-contained component/null. Hooks make this neater but you can still accomplish all of this with class components at the cost of a little verbosity. Since you tagged Typescript this should all be straightforward but you may have to specify that your use of useState is useState<React.ReactChild>(); to avoid type errors.

const useDialog = (ModalComponent) => {
  const [modalDialogState, setModalDialogState] = useState();
  return modalDialogState
    ? [
      setModalDialogState,
      // You may have to tweak this a bit depending on
      // how your library works.
      () => (
        <ModalComponent onClose={() => setModalDialogState('')> 
          {modalDialogState}
        </ModalComponent>
      ),
    ]
    : [setModalDialogState, null];
};

const MyComponent = () => {
  const [setModal, Modal] = useDialog(WhateverLibraryComponent);
  useEffect(() => {
    // Cleanup function, run on unMount and clears dialog contents.
    return () => setModal('');
  }, [setModal]);

  return Modal
    ? (
      <>
        <Modal />
        /* Normal render stuff */
      </>
    )
    // You can optionally render a button here with onClick
    // set to a function that calls setModal with some 
    // appropriate contents.
    : (/* Normal render stuff */)
};
    
Sulk answered 7/9, 2020 at 12:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.