4

The following quick example in TypeScript shows a way to get typed refs without using inline (which are suppose to be bad for performance). It is however rather ugly having to define two variables (refAnimationWrapper and refAnimationWrapperHandler) to define one ref. Does anyone have a simpler solution, could @decorators maybe be a solution?

https://www.typescriptlang.org/docs/handbook/decorators.html

import * as React from 'react';
import {TweenMax} from 'gsap';

export class TransitionDummy extends React.Component<any, any> {
    private transitionDuration = 0.4;

    private refAnimationWrapper:HTMLDivElement;
    private refAnimationWrapperHandler = (ref:HTMLDivElement) => this.refAnimationWrapper = ref;

    constructor(props) {
        super(props);
    }

    public componentWillEnter(done) {
        TweenMax.fromTo(this.refAnimationWrapper, this.transitionDuration, {opacity: 0}, {opacity: 1, onComplete: done});
    }

    public componentWillLeave(done) {
        TweenMax.to(this.refAnimationWrapper, this.transitionDuration, {opacity: 0, onComplete: done});
    }
    
    public render() {
        return (
            <div ref={this.refAnimationWrapperHandler}>
                {this.props.children}
            </div>
        );
    }
}
Lasse
  • 41
  • 2
  • 2
    Why is doing `
    this.refAnimationWrapper = input}>` worse for performance than `
    `?
    – David Sherret Dec 13 '16 at 22:06
  • 2
    Oh nevermind, I see people talking about it [here](https://stackoverflow.com/questions/36677733/why-jsx-props-should-not-use-arrow-functions). Personally, I wouldn't sacrifice code quality over saving a few micro seconds. – David Sherret Dec 13 '16 at 22:12
  • I just use inline lambdas https://medium.com/@basarat/strongly-typed-refs-for-react-typescript-9a07419f807#.t6092k5za – basarat Dec 14 '16 at 07:05
  • @DavidSherret I might end up taking this approach, I was just wondering if there was a way to get both. But probably the performance impact is minimal. – Lasse Dec 14 '16 at 17:38
  • @basarat yeah, that was what I was trying to avoid. – Lasse Dec 14 '16 at 17:38

1 Answers1

2

You can wrap both of them in a class, that way each ref has one member:

class RefedElement<T> {
    wrapper: T;
    handler = (ref: T) => this.wrapper = ref;
}

export class TransitionDummy extends React.Component<any, any> {
    private transitionDuration = 0.4;

    private refAnimation: RefedElement<HTMLDivElement>;

    constructor(props) {
        super(props);
        this.refAnimation = new RefedElement<HTMLDivElement>();
    }

    public componentWillEnter(done) {
        TweenMax.fromTo(this.refAnimation.wrapper, this.transitionDuration, {opacity: 0}, {opacity: 1, onComplete: done});
    }

    public componentWillLeave(done) {
        TweenMax.to(this.refAnimation.wrapper, this.transitionDuration, {opacity: 0, onComplete: done});
    }

    public render() {
        return (
            <div ref={ this.refAnimation.handler }>
                {this.props.children}
            </div>
        );
    }
}
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • Does this code compile for you, I'm getting: `Error:(21, 9) TS2322:Type 'RefedElement<{}>' is not assignable to type 'RefedElement'. Type '{}' is not assignable to type 'HTMLDivElement'. Property 'align' is missing in type '{}'.` – Lasse Dec 14 '16 at 17:36
  • I also don't think this code saves much typing, other than if we could move the new RefedElement() call directly to the variable definition. – Lasse Dec 14 '16 at 17:37
  • Right, it should be `new RefedElement()`, fixed in the answer. And it does save the amount of code once you have more than one "RefedElement". – Nitzan Tomer Dec 14 '16 at 17:41