React HOC:将数据属性传递给包装组件的第一个 child/element

React HOC: Pass data attributes to the first child/element of wrapped component

我有一个这样的 hoc 组件:

export const withAttrs = (WrappedComponent) => {
  const ModifiedComponent = (props) => (
    <WrappedComponent {...props} data-test-id="this-is-a-element" />
  );

  return ModifiedComponent;
};

export default withAttrs;

我是这样使用它的:

import React from 'react';
import withAttrs from './withAttrs';

const SomeLink = () => <a><p>hey</p</a>;

export default withAttrs(SomeLink);

我希望有这样的锚标签:

<a data-test-id="this-is-a-element"><p>hey</p></a>

但是 hoc 不会将 data-attribute 添加到第一个元素。有办法实现吗?

But the hoc doesn't add the data-attribute to the first element.

不是 HOC 没有添加它,而是 SomeLink,它对 HOC 传递给它的道具没有任何作用。

简单的答案是更新SomeLink:

const SomeLink = (props) => <a {...props}><p>hey</p></a>;

到目前为止,这是比以下更好的做法。

如果你做不到,你 可以 让你的 HOC 在事后添加 属性,但让 HOC 到达内部似乎不合适组件和改变的东西。事实上,React 使它创建的元素对象不可变,这强烈建议你不要试图弄乱它们。

仍然有可能,这可能只是个坏主意:

export const withAttrs = (WrappedComponent) => {
    const ModifiedComponent = (props) => {
        // Note we're *calling* the function, not just putting it in
        // a React element via JSX; we're using it as a subroutine of
        // this component rather than as its own component.
        // This will only work with function components. (You could
        // write a version that handles class components as well,
        // but offhand I don't think you can make one HOC that handles
        // both in this case.)
        const result = WrappedComponent(props);
        return {
            ...result,
            props: {
                ...result.props,
                "data-test-id": "this-is-a-element",
            },
        };
    };

    return ModifiedComponent;
};

/*export*/ const withAttrs = (WrappedComponent) => {
    const ModifiedComponent = (props) => {
        // Note we're *calling* the function, not just putting it in
        // a React element via JSX; we're using it as a subroutine of
        // this component rather than as its own component.
        // This will only work with function components. (You could
        // write a version that handles class components as well,
        // but offhand I don't think you can make one HOC that handles
        // both in this case.)
        const result = WrappedComponent(props);

        // THIS IS PROBABLY A VERY BAD IDEA. React makes these objects
        // immutable, probably for a reason. We shouldn't be mucking
        // with them.
        return {
            ...result,
            props: {
                ...result.props,
                "data-test-id": "this-is-a-element",
            },
        };
    };

    return ModifiedComponent;
};

const SomeLink = () => <a><p>hey</p></a>;

const SomeLinkWrapped = withAttrs(SomeLink);

const Example = () => {
    return <div>
        <div>Unwrapped:</div>
        <SomeLink />
        <div>Wrapped:</div>
        <SomeLinkWrapped />
    </div>;
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
/* So we can see that it was applied */
[data-test-id=this-is-a-element] {
    color: green;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

同样,我不认为我会这样做,除非作为 非常 最后的手段,如果它在 React 的未来版本中出现问题,我不会感到惊讶。