代码之家  ›  专栏  ›  技术社区  ›  Afsanefda

React HOC:将数据属性传递给封装组件的第一个子元素

  •  0
  • Afsanefda  · 技术社区  · 4 年前

    我有一个 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 至第一元件。有办法做到这一点吗?

    1 回复  |  直到 4 年前
        1
  •  2
  •   T.J. Crowder    4 年前

    但是hoc不会将数据属性添加到第一个元素中。

    不是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的未来版本中崩溃,我也不会感到惊讶。

    推荐文章