简体   繁体   English

反应关闭父级的所有子模式

[英]React close all child modal from parent

I've three components with the following tree:我有以下树的三个组件:

<Update>
  <ExpenseItem>
    <ExpenseItemModal>

Update takes an array of expenses and render a ExpenseItem component for each expense. Update 接受一组费用并为每项费用呈现一个 ExpenseItem 组件。 I'm using an hook to handle modal visibility.我正在使用钩子来处理模态可见性。 As you can expect, i'm using this modal to edit the expense attributes.正如您所料,我正在使用此模式来编辑费用属性。

A toggle method is imported from useModal hook on ExpenseItem to open and close the modal.从 ExpenseItem 上的 useModal 钩子导入切换方法以打开和关闭模式。 What I expect is to click outside of the modal and close it.我期望的是在模态之外单击并关闭它。 But if I've another ExpenseItem with the modal set to true, it will close the current, but it will still show the other one.但是如果我有另一个 ExpenseItem 模式设置为 true,它会关闭当前,但它仍然会显示另一个。 I want to click outside of the modal (maybe on Update component) and close all modals at once, to avoid multiple modals opened.我想在模态之外单击(可能在更新组件上)并一次关闭所有模态,以避免打开多个模态。 Actually I want only on modal open at once.实际上我只想立即打开模态。

These are the following components:这些是以下组件:

Upload上传

import { useState, useEffect } from 'react';
import useModal from '../hooks/useModal';
import ExpenseItem from './expenseItem';
import axios from 'axios';

function Update({ data }) {
    useEffect(() => console.log('update component', expenses));
    const saveToDatabase = () => {
        axios.post('http://localhost:3001/expenses', expenses).then((res) => {
            console.log('data is saved to database');
        });
    };
    const { setIsShowing } = useModal();
    const closeModals = () => setIsShowing(false);
    const [ expenses, setExpenses ] = useState(data);
    return (
        <div>
            {expenses.map((expense, index) => {
                return <ExpenseItem key={index} index={index} expenses={expenses} setExpenses={setExpenses} />;
            })}
            <button onClick={() => saveToDatabase()}>Save</button>
        </div>
    );
}

export default Update;

ExpenseItem费用项目

import useModal from '../hooks/useModal';
import EditExpenseModal from './editExpenseModal';

function ExpenseItem(props) {
    const { isShowing, toggle, setIsShowing } = useModal();
    let { description, date, credit, debit } = props.expenses[props.index];
    const updateValue = (expense, setExpenses, success) => {
        const expenses = [ ...props.expenses ];
        expenses.splice(props.index, 1, {
            ...expense
        });
        setExpenses(expenses);
        success();
    };
    return (
        <div>
            <div className="expense-box" onClick={toggle}>
                <p>{date}</p>
                <div className="expense-info">
                    <p className="expense-info--description">{description}</p>
                    <p className="expense-info--debit">{debit}</p>
                    <p className="expense-info--credit">{credit}</p>
                </div>
            </div>
            <EditExpenseModal
                isShowing={isShowing}
                hide={toggle}
                expense={props.expenses[props.index]}
                updateExpense={updateValue}
                setExpenses={props.setExpenses}
            />
            <style jsx>{`
                .expense-box {
                    width: 800px;
                    border: 1px solid black;
                    border-radius: 2px;
                    margin: 25px auto;
                    padding: 0 10px;
                }
                .expense-info {
                    display: flex;
                }
                .expense-info--description {
                    margin: 0 auto 0 0;
                }
                .expense-info--debit {
                    color: red;
                }
                .expense-info--credit {
                    color: green;
                }
            `}</style>
        </div>
    );
}

export default ExpenseItem;

EditExpenseModal编辑费用模式

import { useState, useEffect, Fragment } from 'react';
import { createPortal } from 'react-dom';

const EditExpenseModal = ({ expense, isShowing, hide, updateExpense, setExpenses }) => {
    const { description, date, credit, debit } = expense;
    useEffect(() => {
        document.body.style.overflow = 'hidden';
        return () => (document.body.style.overflow = 'unset');
    }, []);
    const [ expenseItem, setExpenseItem ] = useState({
        date,
        description,
        category: null,
        subcategory: null,
        credit,
        debit
    });
    const handleInputChange = (e) => {
        const { name, value } = e.target;
        setExpenseItem({ ...expenseItem, [name]: value });
    };
    return isShowing
        ? createPortal(
                <Fragment>
                    <div>
                        <div className="form">
                            <form>
                                <ul>
                                    <li className="form-inputs">
                                        <label>Date</label>
                                        <input type="text" name="date" defaultValue={date} onChange={handleInputChange} />
                                    </li>
                                    <li className="form-inputs">
                                        <label>Description</label>
                                        <input
                                            type="text"
                                            name="description"
                                            defaultValue={description}
                                            onChange={handleInputChange}
                                        />
                                    </li>
                                    <li className="form-inputs">
                                        <label>Category</label>
                                        <input type="text" name="category" onChange={handleInputChange} />
                                    </li>
                                    <li className="form-inputs">
                                        <label>Subcategory</label>
                                        <input type="text" name="subcategory" onChange={handleInputChange} />
                                    </li>
                                    <li className="form-inputs">
                                        <label>Credit</label>
                                        <input
                                            type="text"
                                            name="credit"
                                            defaultValue={credit}
                                            onChange={handleInputChange}
                                        />
                                    </li>
                                    <li className="form-inputs">
                                        <label>Debit</label>
                                        <input
                                            type="text"
                                            name="debit"
                                            defaultValue={debit}
                                            onChange={handleInputChange}
                                        />
                                    </li>
                                </ul>
                            </form>
                            <button onClick={() => updateExpense(expenseItem, setExpenses, hide)}>save</button>
                            <button onClick={hide}>close</button>
                        </div>
                        <style jsx>{`
                            .form {
                                background: grey;
                                display: flex;
                                flex-direction: column;
                                position: absolute;
                                height: 100vh;
                                top: 0;
                                right: 0;
                                width: 40%;
                            }
                            .form-inputs {
                                display: flex;
                                flex-direction: column;
                                list-style-type: none;
                                padding: 1rem 2rem;
                            }
                        `}</style>
                    </div>
                </Fragment>,
                document.body
            )
        : null;
};

export default EditExpenseModal;

useModal Hook使用模态钩子

import { useState } from 'react';

const useModal = () => {
    const [ isShowing, setIsShowing ] = useState(false);

    function toggle() {
        setIsShowing(!isShowing);
    }

    return {
        isShowing,
        setIsShowing,
        toggle
    };
};

export default useModal;

I don't mind to change these modal structure to make it work.我不介意改变这些模态结构以使其工作。

In this case, to avoid these scenarios you can write a separate method to close modal,在这种情况下,为了避免这些情况,您可以编写一个单独的方法来关闭模态,

inside ExpenseItem.js在 ExpenseItem.js 中

<EditExpenseModal
                isShowing={isShowing}
                hide={hideModal}  //instead of toggle
                ...
 >

and write hideModal method to close modal by passing directly 'false' value instead of using!并通过直接传递“false”值而不是使用来编写 hideModal 方法来关闭模态! operator.操作员。 like this in useModal Hook :像这样在 useModal Hook 中:

function hideModal() {
        setIsShowing(false);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM