Saturday, July 11, 2020

regularly updating in react with pure functional components

I think it's fair to say that playing nicely with setTimeout() and setInterval() is not React's sweetspot, especially when using pure functional components. I feel like I might be missing something (but even Dan Abramov seems to agree) but either something's inside the render (using useState, but being reset all the time in render) or it's outside, and it can't easily hit the state, or you do a mishmash and get an infinite loop of rerenders...

Making things a bit trickier was I needed to instances of these things (I might not have noticed how tricky this was if I was accidentally only using it in a singleton kind of way)

I think this works ok, for a little timer bar that sweeps across its parent...

import React, { useState } from 'react';
import styles from './RiffGrid.module.css';
import { millis2pixels } from '../Utils.js';

const CurrentTimePointer = ({ startTime, isPlaying }) => {
    const [nowish, setNowish] = useState(0);
    let timerRef = null;

    if (isPlaying) {
        timerRef = setTimeout(() => setNowish(Date.now()), 100);
    }
    if (!isPlaying) {
        clearTimeout(timerRef);
        return null;
    }

    return <div className={styles.timePointer} style={{ left: `${millis2pixels(nowish - startTime)}px` }}></div>;
};
export default CurrentTimePointer;

The trick seemed to be setting state with something from outside the system, in this case Date.now(); trying to pull from state for a simple increment gets all confused.

No comments:

Post a Comment