Reactstrap: Reset Modal field on close/open in a functional wrapper component

49 Views Asked by At

In parent component that displays list of todo tasks we can either add or edit existing task. For that a separate wrapper was created.

import React, { useEffect, useState } from 'react';
import {
    Label,
    Input,
    Button,
    Modal,
    ModalHeader,
    ModalBody,
    ModalFooter
} from 'reactstrap';
import DateTimePicker from 'react-datetime-picker';
import { ToDoTask } from '../models/api';

export interface ToDoItemDialoProps {
    visible: boolean,
    item: ToDoTask | undefined,
    onSave: (item: ToDoTask) => void,
    onCancel: () => void
}
const ToDoItemDialog: React.FC<ToDoItemDialoProps> = (props: ToDoItemDialoProps) => {
    const [title, setTitle] = useState('');
    const [description, setDescription] = useState('');
    const [dueDate, setDueDate] = useState<Date>();
    var id = 0;
    /*
    useEffect(() => {
        console.log('useEffect called');
    }, [title, description, dueDate ]);
    */
    return (
        <React.Fragment>
            <Modal isOpen={props.visible} autoFocus={false} toggle={props.onCancel}> 
                <ModalHeader>Add your TODO note</ModalHeader>
                <ModalBody>
                    <Label>Title</Label>
                    <Input
                        autoFocus={true}
                        onChange={e => setTitle(e.target.value)}
                        value={title}
                    />
                    <Label>Optional description</Label>
                    <Input
                        name="text"
                        type="textarea"
                        rows="5"
                        onChange={e => setDescription(e.target.value)}
                        value={description}
                    />
                    <Label>Due by</Label>
                    <DateTimePicker format='dd-MM-yyyy h:mm:ss a'
                        onChange={dateTime => setDueDate(dateTime)}
                        minDate={new Date()}
                        value={dueDate}
                    />
                </ModalBody>
                <ModalFooter>
                    <Button color="primary" onClick={ () => props.onSave({ id: id, title: title, description: description, dueDate: dueDate })}>
                        Save
                    </Button>{' '}
                    <Button color="secondary" onClick={props.onCancel}>
                        Cancel
                    </Button>
                </ModalFooter>
            </Modal>
        </React.Fragment>
    );
}
export default ToDoItemDialog;

Using paradigm that input values should save to state on change and value bound to state I can't figure out how to cleanup all fields once dialog opens again. (Inputs don't seem to need value bound and it solves the problem if we remove value, but DateTimePicker won't display value if selected with calendar.) It opens when parent component sets its visible prop. Basically whenever Ok or Cancel is pressed and then dialog is reopened, input fields are populated from its state.

useEffect seems to run after render and than I don't know how to reset state there. Component needn't necessarily be functional, so will making it a class help? Anyway, how to open dialog with empty fields or values passed from props in an item property?

Parent component usage is

<ToDoItemDialog visible={this.state.isModal} item={this.state.currentItem} onSave={this.addToDoItem} onCancel={this.toggle} />

1

There are 1 best solutions below

0
Nickolodeon On

To answer my own question, this can be achieved with useEffect hook:

        if (props.item) {
            const { id, title, description, dueDate } = props.item;
            setId(id);
            setTitle(title || '');
            setDescription(description || '');
            setDueDate(dueDate ? new Date(dueDate) : null);
        } else {
            setId(0);
            setTitle('');
            setDescription('');
            setDueDate(null);
        }
    }, [props.visible]);```