why destroyOnClose={true} not working in React

Viewed 4393

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.

2 Answers

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"));

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 */)
};
    
Related