繁体   English   中英

如何将组件传递给孩子?

[英]How to pass components to children?

我有一个呈现链接和评论表单的简单组件。 单击该链接会聚焦评论表单。 为此,我使用在顶级组件中创建的 ref 并将其传递给两个子组件。 这部分工作正常。

import { useRef } from "react";

export default function Commentable({children}) {
    const textareaRef = useRef();

    return (
      <>
           <CommentableButton formRef={textareaRef} />
           <CommentForm formRef={textareaRef} />
      </>
    )
}

export function CommentableButton({formRef}) {

    function focusForm(e) {
        e.preventDefault();
        formRef.current.focus();
    }

    return (
        <a href="#" onClick={focusForm}>Comment</a>
    )
}

export function CommentForm({formRef}) {
    return (
        <textarea ref={formRef}></textarea>
    )
}

我希望能够像这样将CommentButtonCommentForm传递给孩子:

export default function Commentable({children}) {
    const textareaRef = useRef();

    return (
      {children 
           button={<CommentableButton formRef={textareaRef} />}
           form={<CommentForm formRef={textareaRef} />}
      }
    )
}

所以我可以这样使用它:

<Commentable>

    <div class="card">
        <div class="card-body">
            <h5>Media Title</h5>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur nec enim id dolor elementum imperdiet. Phasellus luctus ante elit, ac egestas diam posuere at.</p>
            <hr />
            {button}
        </div>
        <div class="card-footer">
            {form}
        </div>
    </div>

<Commentable>

我正在构建的应用程序中有许多可评论的实体,我想将所有可评论的行为保留在一个组件中,以便在需要时使用。 我尝试了不同的方法来实现这一点,但似乎无法弄清楚。 我对 React 还很陌生,刚刚开始使用它。 请让我知道是否有更好的方法来做到这一点。 我正在尝试仅使用功能组件和挂钩来做到这一点。 谢谢你的帮助!

这就是forwardRef发挥作用的地方。

Ref 转发是一种通过组件自动将 ref 传递给它的一个子组件的技术。

首先,将评论按钮和评论表单包装在forwardRef函数中:

export const CommentButton = React.forwardRef((_props, ref) => {
  const focusForm = (e) => {
    e.preventDefault();
    ref.current.focus();
  };

  return (
    // eslint-disable-next-line jsx-a11y/anchor-is-valid
    <a href="#" onClick={focusForm}>
      Comment
    </a>
  );
});

export const CommentForm = React.forwardRef((_props, ref) => {
  return <textarea ref={ref}></textarea>;
});

然后创建可注释组件,我们在其中初始化textareaRef并将textareaRef直接传递给子项。 在这里,我们在箭头函数中渲染按钮和表单组件,并使子组件可调用,以便我们可以将这两个组件传递给可注释组件。

export const Commentable = ({ children }) => {
  const textareaRef = React.useRef();
  const button = () => {
    return <CommentButton ref={textareaRef} />;
  };
  const form = () => {
    return <CommentForm ref={textareaRef} />;
  };

  return children(button(), form());
};

或者通过将组件直接作为参数传递:

export const Commentable = ({ children }) => {
  const textareaRef = React.useRef();
  return children(
     <CommentButton ref={textareaRef} />,
     <CommentForm ref={textareaRef} />
  );
};

这是最终的结果代码和实现:

可评论的.js


import React from "react";

export const Commentable = ({ children }) => {
  const textareaRef = React.useRef();
  return children(
    <CommentButton ref={textareaRef} />,
    <CommentForm ref={textareaRef} />
  );
};

export const CommentButton = React.forwardRef((_props, ref) => {
  const focusForm = (e) => {
    e.preventDefault();
    ref.current.focus();
  };

  return (
    // eslint-disable-next-line jsx-a11y/anchor-is-valid
    <a href="#" onClick={focusForm}>
      Comment
    </a>
  );
});

export const CommentForm = React.forwardRef((_props, ref) => {
  return <textarea ref={ref}></textarea>;
});


您可以像这样使用Commentable组件:

import { Commentable } from "./Commentable";

export default function App() {
  return (
    <Commentable>
      {(button, form) => {
        return (
          <div className="card">
            <div className="card-body">
              <h5>Media Title</h5>
              <p>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                Curabitur nec enim id dolor elementum imperdiet. Phasellus
                luctus ante elit, ac egestas diam posuere at.
              </p>
              <hr />
              {button}
            </div>
            <div className="card-footer">{form}</div>
          </div>
        );
      }}
    </Commentable>
  );
}

您正在寻找的模式可能是渲染道具模式。 https://reactjs.org/docs/render-props.html

您经常会看到以这种方式编写的库,其中包装器组件期望单个函数作为子函数,该子函数具有传递给它的特定道具/增强道具。

const Commentable = ({ children }) => {
  const ref = useRef();

  return children({
    button: <Button ref={ref} />,
    form: <Form ref={ref} />
  });
};

<Commentable>
    {({ button, form }) => (
      <>
        <h1>Hello CodeSandbox</h1>
        {button}
        <h2>Start editing to see some magic happen!</h2>
        {form}
      </>
    )}
</Commentable>

你也可以使用钩子:

const useCommentable = () => {
  const ref = useRef();
  return {
    button: <Button ref={ref} />,
    form: <Form ref={ref} />
  };
};

const MyComponent = () => {
     const {button, form} = useCommentable();
     return <>{button}{form}</>
}

我忘记了在这里的钩子示例中是否需要做额外的工作来防止重新渲染。 无论如何,就像 React 经常说的那样.. 稍后优化。

暂无
暂无

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

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