封装 React 父组件 使子组件添加确认框?

需求 封装一个react中使用的组件(函数式组件)
这个父组件内部放一个button 组件当点击父组件的时候,先弹窗,
然后确定弹窗内容后,再执行子组件的按钮点击逻辑

父组件内容

const Show - ()=>{
  const [open, setOpen] = useState(false)

const click = ()=>{

//当我点击父组件的时候就会触发子组件的点击事件
setOpen(true)
}



return <div onClick={click}>
 <Modal open={open}
             title={"当前操作可能存在风险"}
             onOk={() => setOpen(false)}
      >
        <p>当前操作可能存在风险,确定继续操作</p>
      </Modal>
{children}
</div>

}

子组件

<Button onClick={}>
点击我确认
</Button>

在不改造子组件的情况下,可以实现么,请各位大佬指点/

阅读 457
avatarAI BotBETA

要实现这个功能,你需要一个方法来处理子组件的点击事件,并且当弹窗被确认后才执行该事件。由于你不希望改造子组件,你可以通过props将点击事件传递给子组件,并在父组件中控制该事件的执行。

首先,在父组件中定义一个函数来处理子组件的点击事件,但这个函数默认不执行任何操作,直到弹窗被确认。然后,当弹窗被确认时,你可以调用这个处理函数。

这里是如何修改你的父组件以实现这个功能:

import React, { useState } from 'react';

const Show = ({ children }) => {
  const [open, setOpen] = useState(false);
  const [isConfirmed, setIsConfirmed] = useState(false);

  const handleChildClick = () => {
    // 当子组件的点击事件被触发时,这里什么也不做
    // 等待弹窗被确认
  };

  const handleModalOk = () => {
    setOpen(false);
    setIsConfirmed(true);
    
    // 在这里调用子组件应该执行的逻辑
    // 假设子组件的点击事件是通过 props 传递进来的
    // 你可以在这里模拟调用子组件的点击事件
    // 但实际情况下,你需要根据实际情况来决定如何执行子组件的逻辑
    // 例如,你可以通过回调函数或者状态管理库来执行子组件的逻辑
  };

  const handleClick = () => {
    setOpen(true);
    setIsConfirmed(false);
  };

  // 这里假设 children 中有一个 Button 组件,并且它的 onClick 属性可以通过 props 来设置
  const childWithProps = React.Children.map(children, child => {
    if (React.isValidElement(child) && child.type === Button) {
      // 给子组件的 Button 传递一个处理函数,这个函数会在弹窗被确认后执行
      return React.cloneElement(child, {
        onClick: () => {
          handleChildClick(); // 触发子组件的点击事件,但暂时不执行
        },
        disabled: !isConfirmed, // 当未确认时,禁用按钮以防止重复点击
      });
    }
    return child;
  });

  return (
    <div onClick={handleClick}>
      <Modal open={open} title={"当前操作可能存在风险"} onOk={handleModalOk}>
        <p>当前操作可能存在风险,确定继续操作</p>
      </Modal>
      {childWithProps}
    </div>
  );
};

// 假设的 Button 组件
const Button = ({ children, onClick, disabled }) => (
  <button onClick={onClick} disabled={disabled}>
    {children}
  </button>
);

export default Show;

在上面的代码中,我添加了一个isConfirmed状态来追踪弹窗是否被确认。当点击父组件时,弹窗打开并且isConfirmed设置为false。如果子组件的按钮被点击,handleChildClick会被调用,但此时不会执行任何操作。只有当弹窗被确认(handleModalOk被调用)时,isConfirmed会被设置为true,此时你可以决定如何处理子组件的点击事件。

请注意,这里的关键点在于React.cloneElement,它允许你复制一个元素并传递新的props。在这个例子中,我们给子组件的Button传递了一个新的onClick处理函数和disabled属性。

此外,请注意,这个解决方案假设子组件是一个Button组件,并且你能够直接访问并修改它的props。如果子组件是一个复杂的组件树,你可能需要更复杂的逻辑来处理子组件的点击事件。此外,对于实际的应用,你可能需要考虑使用更健壮的状态管理方案(如Redux)来处理跨组件的交互。

1 个回答

这里采用 React.cloneElement结合ref转发。
子组件的肯定是需要修改的,只不过并非是破坏性的修改,只做新增。

const Show: React.FC<PropsWithChildren> = ({ children }) => {
  const [open, setOpen] = useState(false)
  const ref = useRef<{ emitChildrenClick: any }>(null);

  const onClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent> | any) => {
    //当我点击父组件的时候就会触发子组件的点击事件
    setOpen(true)
    ref.current?.emitChildrenClick?.();
  }

  return (
    <div onClick={onClick}>
      <Modal open={open}
        title={"当前操作可能存在风险"}
        onOk={() => setOpen(false)}
      >
        <p>当前操作可能存在风险,确定继续操作</p>
      </Modal>
      {React.cloneElement(children as React.ReactElement, { ref })}
    </div>
  )
}

const Button1 = forwardRef((props, ref) => {
  function onClick(event: React.MouseEvent<HTMLElement, MouseEvent>) {
    event?.stopPropagation?.(); // 阻止事件冒泡
  }

  useImperativeHandle(ref, () => ({
    emitChildrenClick: onClick
  }))

  return (
    <Button onClick={onClick}>
      点击我确认
    </Button>
  )
})
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题