如何在React高阶分量(HOC)中使用refs

时间:2018-01-11 11:41:42

标签: javascript reactjs ref

我有3个不同标记的导航项,但都需要相同的功能 - 按顺序为其中的svg paths制作动画。听起来像使用HOC的好方案。但是,我无法通过从WrappedComponent中的ref调用的function(activePaths)将refs推送到数组。我收到错误TypeError: _this2.activePaths is not a function。这是我在没有使用HOC的情况下尝试为单个导航项目完成的工作codepen

这是HOC的代码,它给了我错误。

export const withNavItem = WrappedComponent => class extends Component {
  constructor() {
    super();
    this.activePaths = this.activePaths.bind(this);
    this.markerPaths = [];
    this.rendered = false;
  }
  componentDidMount() {
    this.rendered = true;
    this.animatePaths();
  }

  animatePaths() {
    const { markerPaths } = this;
    // prepare stroke to be animated
    for (let i = 0; i < markerPaths.length; i++) {
      const path = markerPaths[i];
      const length = path.getTotalLength();
      path.setAttribute('strokeDasharray', length);
      path.style.strokeDashoffset = length;
    }
    // animate stroke
    const markerDrawing = anime({
      targets: markerPaths,
      strokeDashoffset: [anime.setDashoffset, 0],
      easing: 'easeInOutSine',
      duration: 400,
      delay(el, i) { return i * 150; },
      direction: 'alternate',
      loop: false,
    });
  }

  activePaths(el, linkType) {
    if (el === null || this.rendered) {
      return;
    }
    this.markerPaths.push(el);
  }

  render() {
    this.rendered = true;
    return <WrappedComponent {...this.props} />;
  }
};

export default withNavItem;




class NavItemHey extends React.Component {     
  render() {
    return (
      <div>
        <span className="letter-holder">
          <span className="letter">H</span>
          <span className="letter-strokes letter-strokes--h">
            <span className="h-left-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 106 306">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M59,0c0,0,0.1,114-0.5,195.8S58,314.5,58,314.5" />
              </svg>
            </span>
            <span className="h-middle-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 118 106">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M0.1,58c0,0,33.5-0.1,66.8,0.5s63.2,0.5,63.2,0.5" />
              </svg>
            </span>
            <span className="h-right-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 109 304">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M59,0c0,0,0.1,114-0.5,195.8S58,314.5,58,314.5" />
              </svg>
            </span>
          </span>
        </span>

        <span className="letter-holder letter-e">
          <span className="letter">E</span>
          <span className="letter-strokes letter-strokes--e">
            <span className="e-left-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 106 316">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M59,0c0,0,0.1,114-0.5,195.8S58,314.5,58,314.5" />
              </svg>
            </span>
            <span className="e-top-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 134 105">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M0.2,51c0,0,24.5-0.1,57.8,0.5s75.2,0.5,75.2,0.5" />
              </svg>
            </span>
            <span className="e-middle-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 127 103">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M0.2,51c0,0,24.5-0.1,57.8,0.5s75.2,0.5,75.2,0.5" />
              </svg>
            </span>
            <span className="e-bottom-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 136 106">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M0.2,51c0,0,24.5-0.1,57.8,0.5s75.2,0.5,75.2,0.5" />
              </svg>
            </span>
          </span>
        </span>

        <span className="letter-holder letter-y">
          <span className="letter">Y</span>
          <span className="letter-strokes letter-strokes--y">
            <span className="y-left-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 196 232">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M58,0c0,42,13.8,71.5,37,117c24,47,52,80,52,116" />
              </svg>
            </span>
            <span className="y-right-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 218 215">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M110.5,0.1c0,0,0.1,83.6-0.5,143.5c-0.5,60-0.5,90-0.5,90" />
              </svg>
            </span>
            <span className="y-bottom-stroke letter-stroke">
              <svg className="letter-stroke__svg" viewBox="0 0 106 122">
                <path ref={el => this.activePaths(el)} className="letter-stroke__svg-path" d="M59,0.1c0,0,0.1,43.5-0.5,76.8S58,125.6,58,125.6" />
              </svg>
            </span>
          </span>
        </span>
      </div>
    );
  }
};

export default withNavItem(NavItemHeyo);

这个codepen是一个采用不同路径按顺序为某些路径设置动画的示例,但我不确定是否可以复制此路径,因为我的路径是如此嵌套。

有没有人有任何想法?

2 个答案:

答案 0 :(得分:2)

错误显示,因为子组件中的 this 表示子组件而不是HOC。为了能够使用这样的功能,它需要通过props传递给子组件。一些伪代码:

// HOC
...
render() {
  this.rendered = true;
  return <WrappedComponent {...this.props} activePaths={this.activePaths} />;
} 

// WRAPPED COMPONENT
...
<path ref={el => this.props.activePaths(el)} />

答案 1 :(得分:0)

如果我正确理解了您的Codepen,只需在HOC中添加componentDidMount并在其中操作引用。

   const withNavItem = InnerComponent => class extends React.Component {
      constructor() {
        super();
        this.elementsArray = [];
      }

// Added code
      componentDidMount(){
        this.elementsArray.forEach(el => {
          // change appearance, animate, etc...
          el.style.background = 'black'
        })
      }
// ^ added code    
      hocFunc(el, rendered) {
        if (el === null || rendered) { // don't keep adding in refs after rendered first time
          return;
        }
        this.elementsArray.push(el);
        console.log(this.elementsArray);
      }

      render() {
        return <InnerComponent {...this.props} elementsArray={this.elementsArray} hocFunc={this.hocFunc}  />;
      }
    };



    class NavItemOne extends React.Component {
      constructor() {
        super();
        this.rendered = false;
      }

      componentDidMount() {
        this.rendered = true;
      }

      render() {
        return (
          <div>
            <div ref={el => this.props.hocFunc(el, this.rendered)} className="box a" />
            <div ref={el => this.props.hocFunc(el, this.rendered)} className="box b" />
            <div ref={el => this.props.hocFunc(el, this.rendered)} className="box c" />
            <div ref={el => this.props.hocFunc(el, this.rendered)} className="box d" />
          </div>
        );
      }
    };

    const NavItemOneWrapped = withNavItem(NavItemOne);

    ReactDOM.render(
      <NavItemOneWrapped />,
      document.getElementById('root')
    );