【问题标题】:React composition - How to call parent methodReact 组合 - 如何调用父方法
【发布时间】:2022-01-11 17:14:26
【问题描述】:

我在 React 中使用组合并想调用父方法。我发现的所有示例都使用继承。

容器组件 - 插入子组件

interface ContainerProps {
  children: ReactNode;
}

function Container(props: ContainerProps) {
  const [showApply, setShowApply] = useState<boolean>(false);

  return (
    <>
        <div>Children</div>
        {props.children}
    </>
  );

  // I want to call this method from the `children`
  function calledByChild(){}
}

组合 - 点击按钮时需要调用Container方法

function CombinedComponent() {
    
    return <Container handleApplyClicked={handleApplyClicked}>
        <Button type="primary" shape="round" onClick={tellContainerThatButtonWasClicked}>
    </Container >
}

当单击CombinedComponent 中的按钮时,我希望它通知Container。我见过的示例使用继承并将父母方法传递给孩子,但在这种情况下,孩子正在其中定义父母。

如何做到这一点?

更新

我尝试将它添加到父组件,但子组件似乎没有添加额外的属性。

{React.cloneElement(props.children as React.ReactElement<any>, { onClick: myFunc })}

子界面/道具

interface CombinedComponentProps{
  // This value is always undefined
  onClick?: () => void;
} 


function CombinedComponent(props: CombinedComponentProps) {
    ...
    // Undefined
    console.log(props.onClick)
}

【问题讨论】:

    标签: javascript reactjs typescript


    【解决方案1】:

    我最近不得不做一些类似的事情,受this post 的启发,我最终得到了:

    const WrapperComponent = ({ children }) => {
      const myFunc = React.useCallback(() => {
        // ...
      }, []);
      
      return React.Children.map(children, (child) => {
        if (React.isValidElement(child)) {
          return React.cloneElement(child, { onClick: myFunc });
        }
      });
    }
    

    [编辑]一个工作演示:

    以下 sn-p 演示了如何使用上述方法从包装器/父组件中读取子道具。
    请注意,sn-p 加载和运行可能需要几秒钟;我没有调查原因,因为它超出了问题的范围。

    const App = () => {
      return (
        <div>
          <h1>MyApp</h1>
          <WrapperComponent>
            <button id='btn1'>btn1</button>
            <button id='btn2'>btn2</button>
            <button id='btn3'>btn3</button>
            <div className='fakeBtn' id='div1'>div1</div>
          </WrapperComponent>
        </div>
      );
    }
    
    const WrapperComponent = ({ children }) => {
      const [clickedChildId, setClickedChildId] = React.useState();
      
      const myFunc = React.useCallback((id) => {
        setClickedChildId(id)
      }, [setClickedChildId]);
      
      React.useEffect(() => {
        clickedChildId && console.log(`the clicked child ID is ${clickedChildId}`);
      }, [clickedChildId]);
      
      return React.Children.map(children, (child) => {
        if (React.isValidElement(child)) {
          return React.cloneElement(child, { onClick: () => myFunc(child.props.id) });
        }
      });
      
    }
    
    ReactDOM.render(<App />, document.querySelector('#mountNode'))
    div.fakeBtn {
      background-color: #cdf;
      padding: 5px;
      margin: 3px 0;
      border: 1px solid #ccc;
      border-radius: 2px;
    }
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    
    <div id='mountNode'></div>

    【讨论】:

    • 这看起来正是我所需要的。我试过使用它,但该属性似乎没有传递给孩子。我已经更新了这个问题,因为我假设这很明显我错过了。
    • 好吧,如果你需要传递一些值 "up",myFunc 可以接受一个参数并使用它来设置父级中的状态。
    • 我的意思是onClick在使用cloneElement之后在子组件中是未定义的。
    • 抱歉,我忘记了React.cloneElement(...)之前的return;我现在添加了它。
    【解决方案2】:

    你可以通过克隆孩子来做到这一点,并给它你想要的道具:

    React.cloneElement(children, {calledByChild})
    

    这样,您将函数calledByChild 添加到子组件中,因此您可以从子组件中调用它。

    它可能看起来像这样:

    const Parent = ({ children }) => {
      const func = () => console.log("click in Parent");
      return (
        <>
          <div>children</div>
          {cloneElement(children, {func})}
        </>
      );
    };
    
    const Children = ({func}) => {
      return <button onClick={func}>Click</button>;
    };
    

    看看这个article

    【讨论】:

    • 这很有道理,谢谢!您是否会说必须使用这种方法表明我不应该像这样构建我的组件,或者像这样的组合经常使用?
    • 我不建议这样做。但实际上这取决于您的需求。你能说明你在哪里实例化父组件吗?
    • 你可以看一下文档reactjs.org/docs/composition-vs-inheritance.html 通常你直接把孩子给父母,所以你可以直接把道具给孩子,不用这个“hack”
    猜你喜欢
    • 2017-02-27
    • 2018-07-20
    • 1970-01-01
    • 2016-12-03
    • 1970-01-01
    • 2021-05-02
    • 2018-02-22
    • 1970-01-01
    相关资源
    最近更新 更多