Null Salad
Null Salad

Reputation: 1030

How do you pass a React callback function to a typescript object?

I have a singleton class in Daemon.ts

export default class Daemon {
    
    private static instance : Daemon;
    callback : (tstring : string)=>void;
    t : number;

    constructor (callback: (tstring : string)=>void){
        this.data = data
        this.callback = callback;
        this.t = 0;
        window.setInterval(this.tick.bind(this), 1000)
    }

    public static getInstance(callback: (tstring : string)=>void){
        if(!Daemon.instance){ Daemon.instance = new Daemon(callback);}
        return Daemon.instance;
    }

    tick(){
        this.t = this.t + 1
        this.callback(this.t.toString());
    }

}

Then in a separate file, Timefeedback.tsx, I have:

const TimeFeedback = () => {

    const [time, updateSimTime] = React.useState<string>("starting string");
    
    const updateTime = (tString : string) => {
      updateSimTime(tString);
      console.log(`update time called, val: ${tString}`)
    }

    const daemon = Daemon.getInstance(updateTime.bind(this));
  
    return (
      <div>
        {time}
      </div>
    );
  };

What I expect to happen:

What actually happens:

I have investigated, when calling print events inside of Daemon I get:

function() {
    [native code]
}

I tried to remove the .bind(this) inside TimeFeedback.tsx, but nothing changes except the print instead says:

tString => {
    setSimTime(tString);
    console.log(`update time called, val: ${tString}`);
  }

I also walked through with the debugger and updateSimTime does get called but it has no effect.

Why is that updateSimTime has no effect?

Upvotes: 1

Views: 859

Answers (1)

Linda Paiste
Linda Paiste

Reputation: 42170

No need to .bind in the component

updateTime.bind(this) doesn't make sense in a function component because a function component doesn't have a this like a class component does. Your updateTime function can be passed directly to the Daemon.getInstance function.

You also need to remove the this.data = data in the Daemon constructor because there is no data variable` (as pointed out by @Konrad in the comments).

As a best practice, I would recommend moving the code into a useEffect hook.

const TimeFeedback = () => {
  const [time, updateSimTime] = useState<string>("starting string");

  useEffect(() => {
    const updateTime = (tString: string) => {
      updateSimTime(tString);
      console.log(`update time called, val: ${tString}`);
    };
  
    Daemon.getInstance(updateTime);
  }, [updateSimTime]);

  return <div>{time}</div>;
};

CodeSandbox Link

Upvotes: 2

Related Questions