reactjstypescriptantdant-design-pro

onRemove Upload Image antDesign + delete item from interface


I have a project, and this project contains several interfaces, and among these interfaces there is an interface for uploading an image, and the problem is in the deletion icon. When you click on it, a modal appears, but the element is deleted before the modal appears.

How can i solve the problem?

this file display a list of instructions that contains upload Image

import '../../../styles/input/index.scss';
import '../../../styles/dropzone/index.scss';
import { Button, Col, message, Modal, Row, Spin, Upload, UploadFile } from 'antd';
import { FunctionComponent, useCallback, useRef, useState } from 'react';
import { motion, useAnimation } from 'framer-motion';
import { defaultTranstion } from '../../../constants/framer';
import { Controller } from 'react-hook-form';
import FromElemnetWrapper from '../form-element-wrapper';
import { Delete, UploadCloud } from 'react-feather';
import { getBase64 } from '../../../utils/get-base64';
import _ from 'lodash';
import config from '../../../api/nuclearMedicineApi/config';
import { FormattedMessage } from 'react-intl';
import BasicModal from '../modal';
import { UploadOutlined } from '@ant-design/icons';
import axios from 'axios';
import { IFormError } from '../general-form-container';

interface DropzoneProps {
    name: string;
    control: any;
    rules?: any;
    label: string;
    disabled?: boolean;
    multiple?: boolean;
    accept?: string;
    refType?: number;
    defaultFileList?: any;
    onRemove?: any;
    customRequest?: (option: any) => void;
    onProgress?: any;
}


const Dropzone: FunctionComponent<DropzoneProps> = ({
    name,
    control,
    rules,
    label,
    disabled,
    multiple,
    accept,
    refType,
    defaultFileList,
    onRemove,
    customRequest,
    onProgress
}) => {
    const focusController = useAnimation();
    const errorController = useAnimation();
    const [previewVisible, setpreviewVisible] = useState(false);
    const [previewImage, setpreviewImage] = useState('');
    const handleCancel = () => setpreviewVisible(false);
    const handlePreview = async (file: any) => {
        if (!file.url && !file.preview) {
            file.preview = await getBase64(file.originFileObj);
        }
        setpreviewImage(file?.preview ?? file.url);
        setpreviewVisible(true);
    };

    const [isModalOpen, setIsModalOpen] = useState(false);
    const [errors, setErrors] = useState<IFormError[]>([]);

    const [visibleModal, setVisibleModal] = useState(false);
    const [removePromise, setRemovePromise] = useState();
    const [deleteVisible, setdeleteVisible] = useState<boolean>(false);
    
    const onDeleteHandle = () => {

        setdeleteVisible(false);
    };

    const deletehandleCancel = () => {
        setdeleteVisible(false);
    };
    let resolvePromiseRef =  useRef<((value: boolean) => void) | undefined>();
    // let resolvePromiseRef = useRef<HTMLInputElement | null>(null)

    const handleRemove = useCallback(() =>{
        const promise = new Promise(resolve => {
          resolvePromiseRef.current = resolve
        });
        setVisibleModal(true);
        return promise;
      }, [])

      const handleOkModalRemove = useCallback(() => {
        if (resolvePromiseRef.current) {
          resolvePromiseRef.current(true)
        }
      }, [removePromise]);
      const handleCancelModalRemove = useCallback(() => {
        if (resolvePromiseRef.current) {
          resolvePromiseRef.current(false); 
          setVisibleModal(false)
        }
      }, [removePromise]);

    return (
        <>
            <FromElemnetWrapper
                focusController={focusController}
                errorController={errorController}
                label={label}
                required={rules.required?.value}
            >
                <Controller
                    control={control}
                    name={name}
                    rules={rules}
                    render={({
                        field: { onChange, onBlur, value, name, ref },
                        fieldState: { invalid, error },

                    }) => {

                        if (invalid) {
                            errorController.start({ scale: 80 });
                        } else {
                            errorController.start(
                                { scale: 0 },
                                { ease: defaultTranstion.ease.reverse() },
                            );
                        }
                        return (
                            <div
                                onBlur={() => {
                                    onBlur();
                                    focusController.start({ scale: 0 });
                                }}
                                onFocus={() => {
                                    focusController.start({ scale: 80 });
                                }}
                                className='relative'

                            >
                                <div className='upload-container'>
                                    <form
                                        className='dropzone needsclick'
                                        id='demo-upload'
                                        action='/upload'
                                    >
                                        {/* <> */}
                                        <Upload
                                            action={`${config.baseUrl}api/services/app/Attachment/Upload`}
                                            headers={config.headers}
                                            ref={ref}
                                            multiple={multiple}
                                            disabled={disabled}
                                            data={{ RefType: refType }}
                                            listType='picture'
                                            fileList={value}
                                            id={name}
                                            accept={accept}
                                            onPreview={handlePreview}
                                            onRemove={handleRemove}
                                            iconRender={
                                                () => {
                                                    return <Spin style={{ marginBottom: '12px', paddingBottom: '12px' }}></Spin>
                                                }
                                            }
                                            progress={{
                                                strokeWidth: 3,
                                                strokeColor: {
                                                    "0%": "#f0f",
                                                    "100%": "#ff0"
                                                },
                                                style: { top: 12 }
                                            }}
                                            beforeUpload={
                                                (file) => {
                                                    console.log({ file });
                                                    return true
                                                }
                                            }

                                        // onProgress= {(event: any) => (event.loaded / event.total) * 100}
                                        // onChange={(e) =>
                                        //     onChange(e.fileList)
                                        // }


                                        // onChange={(response) => {
                                        //     console.log('response: ', response);

                                        //     if (response.file.status !== 'uploading') {
                                        //         console.log(response.file, response.fileList);
                                        //     }
                                        //     if (response.file.status === 'done') {
                                        //         message.success(`${response.file.name} 
                                        //                        file uploaded successfully`);
                                        //     } else if (response.file.status === 'error') {
                                        //         message.error(`${response.file.name} 
                                        //                      file upload failed.`);
                                        //     }
                                        //     else if (response.file.status === 'removed') {
                                        //         message.error(`${response.file.name} 
                                        //                      file upload removed.`);
                                        //     }
                                        // }}

                                        >
                                            <div className='upload-button'>
                                                <div className='wrapper'>
                                                    <motion.div
                                                        className='fas fa-angle-double-up'
                                                        whileHover={{
                                                            y: [
                                                                0, -2, 2,
                                                                0,
                                                            ],
                                                            transition: {
                                                                duration: 1.5,
                                                                ease: 'easeInOut',
                                                                yoyo: Infinity,
                                                            },
                                                        }}
                                                    >
                                                        <UploadCloud
                                                            style={{
                                                                margin: '.2rem',
                                                                display:
                                                                    'inline-block',
                                                            }}
                                                            color='white'
                                                            size={20}
                                                        />
                                                        Upload
                                                    </motion.div>
                                                </div>
                                            </div>
                                        </Upload>
                                        {/*
                                        <Modal
                                            title="Are you sure?"
                                            visible={visibleModal}
                                            onOk={handleOkModalRemove}
                                            onCancel={handleCancelModalRemove}
                                        /> */}

                                        <BasicModal
                                            header={
                                                <>
                                                    <FormattedMessage id={'confirmdeletion'} />
                                                </>
                                            }
                                            headerType='error'
                                            content={
                                                <>
                                                    <Row>
                                                        <Col span={8} offset={4}>
                                                            <Button
                                                                type='primary'
                                                                className='savebtn'
                                                                onClick={onDeleteHandle}
                                                                style={{
                                                                    cursor:
                                                                        Object.keys(errors).length !==
                                                                            0
                                                                            ? 'not-allowed'
                                                                            : 'pointer',
                                                                }}
                                                            >
                                                                <FormattedMessage id={'affirmation'} />
                                                            </Button>
                                                        </Col>
                                                        <Col span={8} offset={4}>
                                                            <Button
                                                                type='default'
                                                                className='savebtn'
                                                                onClick={deletehandleCancel}
                                                                style={{
                                                                    cursor:
                                                                        Object.keys(errors).length !==
                                                                            0
                                                                            ? 'not-allowed'
                                                                            : 'pointer',
                                                                }}
                                                            >
                                                                <FormattedMessage id={'cancel'} />
                                                            </Button>
                                                        </Col>
                                                    </Row>
                                                </>
                                            }
                                            isOpen={visibleModal}
                                            footer={false}
                                            width='35vw'
                                        handleCancel={handleCancelModalRemove}
                                        handleOk={handleOkModalRemove}
                                        />

                                        {/* {_.isEmpty(value) && (
                                            <div className='dz-message needsclick'>
                                                <FormattedMessage id='dropfileshere' />
                                            </div>
                                        )} */}
                                        <BasicModal
                                            isOpen={previewVisible}
                                            header={<FormattedMessage id="Preview image" />}
                                            footer={false}
                                            handleCancel={handleCancel}
                                            content={<img
                                                alt='example'
                                                style={{ width: '100%' }}
                                                src={previewImage}
                                            />}
                                        />
                                        {/* </> */}

                                    </form>
                                </div>
                                {invalid && (
                                    <p className='form-element-error'>
                                        {error?.message}
                                    </p>
                                )}
                            </div>
                        );
                    }}
                />
            </FromElemnetWrapper>
        </>
    );
};

export default Dropzone;

Solution

  • Why it doesn't work?

    https://ant.design/components/upload

    onRemove - A callback function, will be executed when removing file button is clicked, remove event will be prevented when return value is false or a Promise which resolve(false) or reject

    You have to return a promise which resolves to false or return false

    How to solve it?

    You have to return a promise so first of all, you need a ref or a state to store resolve function of that promise so you can call it in modal

    const resolvePromiseRef = useRef<((value: boolean) => void) | undefined>();
    

    Then you will need to assign the resolve function to the ref and return the promise

    Now onRemove will wait for your promise to resolve

    const handleRemove = () =>{
      const promise = new Promise(resolve => {
        resolvePromiseRef.current = resolve
      });
      setVisibleModal(true);
      return promise;
    }
    

    Now your functions handlers

    const handleOkModalRemove = useCallback(() => {
      if (resolvePromiseRef.current) {
        resolvePromiseRef.current(true)
      }
    }, [removePromise]);
    const handleCancelModalRemove = useCallback(() => {
      if (resolvePromiseRef.current) {
        resolvePromiseRef.current(false)
      }
    }, [removePromise]);
    

    You can also use the state, but I recommend ref because it doesn't rerender the component when changed.