React - 从子级访问父级的状态,无需嵌套函数

发布于 2025-01-16 16:08:45 字数 91 浏览 0 评论 0原文

你好,
我今天第一次来找你,因为我还没有找到解决我的问题的方法。
我已经使用 React 几个星期了,不要对我的代码质量太残酷

Hello,
I am coming to you today for the first time because I have not found a solution to my problem.
I have been using react for a few weeks, Don't be too cruel about the quality of my code ????.

Problem :
I am looking to access the state of a parent from their children.So I want to be able to access the setHeight function and the height variable for example from a child component.

Please note :
However, to keep some flexibility, I don't want to have any Components inside our.
I looked at redux to be able to do this, but the problem is that the data is global so creating multiple dropdowns would not be possible.
(Unless I didn't understand too much, redux is quite complex)

Diagram :
I have created a diagram to explain it a little better.,
I'd like the children of DropdownMenu to be able to access the state of the latter, Also, the different Dropdowns must have their own state independently.
So ideally I want to keep the same structure as find very flexible, and the possibility to create several dropdown.

enter image description here


Code :
I Share my four components :


export default function Navbar () {
    return (
        <nav className={styles.navbar}>
            <ul className={styles.navbarNav}>
                <NavItem icon={<NotificationsIcon />} />
                <NavItem icon={<AccessTimeFilledIcon />} />
                <NavItem icon={<FileOpenIcon />}>
                    <DropdownMenu>
                        <DropdownSubMenu menuName="Home">
                            <DropdownItem>My Profile</DropdownItem>
                            <DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="pages">Pages</DropdownItem>
                            <DropdownItem>IDK</DropdownItem>
                            <DropdownItem>Test</DropdownItem>
                        </DropdownSubMenu>
                        <DropdownSubMenu menuName="pages">
                            <DropdownItem>Pages</DropdownItem>
                            <DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="home">Home</DropdownItem>
                        </DropdownSubMenu>
                    </DropdownMenu>

                    <DropdownMenu>
                        <DropdownSubMenu menuName="config">
                            <DropdownItem>Foo</DropdownItem>
                            <DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="theme">Configuration</DropdownItem>
                            <DropdownItem>Bar</DropdownItem>
                            <DropdownItem>Baz</DropdownItem>
                        </DropdownSubMenu>
                        <DropdownSubMenu menuName="theme">
                            <DropdownItem>Hi StackOverflow</DropdownItem>
                            <DropdownItem leftIcon={<AccessTimeFilledIcon />} rightIcon={<ChevronRightIcon />} goToMenu="config">Theme</DropdownItem>
                        </DropdownSubMenu>
                    </DropdownMenu>
                </NavItem>
            </ul>
        </nav>
    );
};

type Props = {
    children?: React.ReactNode | React.ReactNode[];
    leftIcon?: React.ReactNode | JSX.Element | Array<React.ReactNode | JSX.Element>;
    rightIcon?: React.ReactNode | JSX.Element | Array<React.ReactNode | JSX.Element>;
    goToMenu?: string;
    goBack?: boolean;
    OnClick?: () => void;
};

export default function DropdownItem({ children, leftIcon, rightIcon, goToMenu, goBack, OnClick }: Props) {
    const handleClick = OnClick === undefined ? () => { } : OnClick;

    return (
        <a className={styles.menuItem} onClick={() => {
            goToMenu && setActiveMenu(goToMenu);
            setDirection(goBack ? 'menu-right' : 'menu-left');
            handleClick();
        }}>
            <span className={styles.iconButton}>{leftIcon}</span>
            {children}
            <span className={styles.iconRight}>{rightIcon}</span>
        </a>
    );
}

type Props = {
    menuName: string;
    children: React.ReactNode | React.ReactNode[];
}

enum Direction {
    LEFT = 'menu-left',
    RIGHT = 'menu-right'
}

export default function DropdownSubMenu (props: Props) {
    const [direction, setDirection] = useState<Direction>(Direction.LEFT);
    
    const calcHeight = (element: HTMLElement) => {
        if (element) setMenuHeight(element.offsetHeight);
    };

    return (
        <CSSTransition in={activeMenu === props.menuName} unmountOnExit timeout={500} classNames={direction} onEnter={calcHeight}>
            <div className={styles.menu}>
                {props.children}
            </div>
        </CSSTransition>
    );
}

type Props = {
    children: React.ReactNode | React.ReactNode[];
}

export default function DropdownMenu (props: Props) {
    const [activeMenu, setActiveMenu] = useState<string>('home');
    const [menuHeight, setMenuHeight] = useState<number | null>(null);
    const dropdownRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        const child = dropdownRef.current?.firstChild as HTMLElement;
        const height = getHeight(child);

        if (height)
            setMenuHeight(height);
    }, []);

    return (
        <div className={styles.dropdown} style={{ height: `calc(${menuHeight}px + 2rem)` }} ref={dropdownRef}>
            {props.children}
        </div>
    );
}

Conclusion :
More concretely I don't know what to put instead :

In DropdownSubMenu to set the menu height (setMenuHeight), and gets the active menu (activeMenu).
In DropdownItem, set the active menu, (setActiveMenu) and set the direction of the CSS animation (setDirection).

Source :
My code is adapted from these sources, But I want to make this code more professional, flexible and polymorphic :
https://github.com/fireship-io/229-multi-level-dropdown

I've been tried :
I tried to look at Redux, but I understood that it was only state global.
So it doesn't allow to define a different context for each component.

I tried to look at React 18, without success.
I have searched the StackOverflow posts, I have searched the state retrieval from the parents.

The use of components inside a component solves in effect the problem but we lose all the flexibility.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

抱猫软卧 2025-01-23 16:08:45

有多种方法可以从子状态访问父状态。

将状态作为 props 传递

首选方法是将状态和/或更改函数传递给子级。

示例:

const App = () => {
    const [open, setOpen] = React.useState(false);

    const handleOpen = () => setOpen(true);
    const handleClose = () => setOpen(false);

    return (
        <div>
            <button onClick={handleOpen}>Open modal</button>
            <Modal onClose={handleClose} open={open} />
        </div>
    );
};

const Modal = ({ open, onClose }) => (
    <div className={open ? "open" : "close"}>
        <h1>Modal</h1>
        <button onClick={onClose}>Close</button>
    </div>
);

ReactDOM.render(<App />, document.querySelector("#app"));

演示: https://jsfiddle.net/47s28ge5/1/

使用 React Context

第一个当子级嵌套很深并且您不想沿着组件树携带状态时,方法会变得复杂。

然后,您可以使用上下文在多个子级之间共享状态。

const AppContext = React.createContext(undefined);

const App = () => {
    const [open, setOpen] = React.useState(false);

    const handleOpen = () => setOpen(true);
    const handleClose = () => setOpen(false);

    return (
        <AppContext.Provider value={{ open, onClose: handleClose }}>
            <div>
                <button onClick={handleOpen}>Open modal</button>
                <Modal />
            </div>
        </AppContext.Provider>
    );
};

const Modal = () => {
    const { open, onClose } = React.useContext(AppContext);

    return (
        <div className={open ? "open" : "close"}>
            <h1>Modal</h1>
            <button onClick={onClose}>Close</button>
        </div>
    );
};

ReactDOM.render(<App />, document.querySelector("#app"));

演示:https://jsfiddle.net/dho0tmc2/3/

使用减速器

如果您的代码得到更复杂的是,您可能会考虑使用存储在组件之间共享全局状态。

您可以查看流行的选项,例如:

There are multiple ways to access a parent state from its children.

Pass the state as props

The preferred way is to pass the state and/or the change function to the children.

Example :

const App = () => {
    const [open, setOpen] = React.useState(false);

    const handleOpen = () => setOpen(true);
    const handleClose = () => setOpen(false);

    return (
        <div>
            <button onClick={handleOpen}>Open modal</button>
            <Modal onClose={handleClose} open={open} />
        </div>
    );
};

const Modal = ({ open, onClose }) => (
    <div className={open ? "open" : "close"}>
        <h1>Modal</h1>
        <button onClick={onClose}>Close</button>
    </div>
);

ReactDOM.render(<App />, document.querySelector("#app"));

Demo: https://jsfiddle.net/47s28ge5/1/

Use React Context

The first method becomes complicated when the children are deeply nested and you don't want to carry the state along the component tree.

You can then share a state across multiple children by using context.

const AppContext = React.createContext(undefined);

const App = () => {
    const [open, setOpen] = React.useState(false);

    const handleOpen = () => setOpen(true);
    const handleClose = () => setOpen(false);

    return (
        <AppContext.Provider value={{ open, onClose: handleClose }}>
            <div>
                <button onClick={handleOpen}>Open modal</button>
                <Modal />
            </div>
        </AppContext.Provider>
    );
};

const Modal = () => {
    const { open, onClose } = React.useContext(AppContext);

    return (
        <div className={open ? "open" : "close"}>
            <h1>Modal</h1>
            <button onClick={onClose}>Close</button>
        </div>
    );
};

ReactDOM.render(<App />, document.querySelector("#app"));

Demo: https://jsfiddle.net/dho0tmc2/3/

Using a reducer

If your code gets even more complicated, you might consider using a store to share a global state across your components.

You can take a look at popular options such as:

停滞 2025-01-23 16:08:45

我可以说欢迎此时此刻做出反应,我为你感到高兴

好的,我可以理解你的问题是什么。
但没有问题,这个错误是由于你的经验不足造成的。

据我了解,您想要单击下拉菜单并将其打开。
这里我们有嵌套的下拉菜单。

我想这就是你的答案:
您应该在每个下拉列表中声明一个状态,而不是在父级中声明状态。

I can say welcome to react in this moment and i glad for you

OK, i could understand what is your problem.
but there isn't problem and this bug cause from your low experience.

As i understand you want to click on a dropdown and open it.
and here we have nested dropdown.

I think it's your answer:
You should declare a state on each dropdown and don't declare state in parent.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文