reactjs - Clase de Typescript como estado de React, volver a renderizar
Tengo el siguiente componente de función:
const Player = () => {
const [spotifyPlayer, setSpotifyPlayer] = React.useState<SpotifyPlayer | null>(null);
return (
<SongControls togglePlay={togglePlay} isPlaying={spotifyPlayer.isPlaying} />
);
};
el componente SongControls es responsable de mostrar el botón de control correcto (iniciar/pausar):
const SongControls: React.FC<Props> = ({ isPlaying }: Props) => {
return (
{isPlaying ? (
<Pause style={iconBig} onClick={togglePlay} />
) : (
<PlayArrow style={iconBig} onClick={togglePlay} />
)}
);
};
El estado spotifyPlayer en el componente Player es una clase que tiene un campo isPlaying de tipo boolean. También tiene una función definida togglePlay() que alterna isPlaying.
El problema es que cuando se llama a togglePlay(), React no vuelve a renderizar los componentes. Entiendo por qué (ya que la instancia de SpotifyPlayer no está cambiando, por eso no deberías actualizar el estado de React directamente).
No puedo llamar a setSpotifyPlayer() para actualizar el estado por 2 razones:
- la instancia de
SpotifyPlayeres responsable de alternarisPlaying - cuando cambio el estado usando la desestructuración de js pierdo el prototipo del estado
spotifyPlayery la función ya no estará disponible.
No puedo resolver esto.
Respuesta #1
Tu comprensión del problema es correcta. No puedes depender de los cambios en el estado interno del objeto SpotifyPlayer para disparar una nueva renderización. Recomiendo usar un estado local para almacenar el valor de isPlaying y actualizar el SpotifyPlayer en función de los cambios en ese valor.
Si el objeto del reproductor tiene algún tipo de listener onChange, entonces usaría el reproductor para establecer el estado local en lugar de al revés.
No estoy seguro de qué paquete proviene el jugador, así que estoy asumiendo esta interfaz:
class SpotifyPlayer {
isPlaying: boolean = false;
play(): void {
this.isPlaying = true;
}
pause(): void {
this.isPlaying = false;
}
}
Podemos usar useRef para almacenar una referencia constante a un objeto jugador.
const spotifyPlayer = React.useRef<SpotifyPlayer>(new SpotifyPlayer()).current;
Almacenaremos el valor de isPlaying en el estado local. Podemos obtener el valor inicial del reproductor.
const [isPlaying, setIsPlaying] = React.useState(spotifyPlayer.isPlaying);
Para mantener isPlaying sincronizado con el reproductor, podríamos usar useEffect.
const togglePlay = () => setIsPlaying(!isPlaying);
React.useEffect(() => {
if ( isPlaying && ! spotifyPlayer.isPlaying ) {
spotifyPlayer.play();
} else if ( ! isPlaying && spotifyPlayer.isPlaying ) {
spotifyPlayer.pause();
}
}, [spotifyPlayer, isPlaying]);
Pero si togglePlay es la única función que cambiará el valor, entonces podríamos manejar los cambios en esa función.
const togglePlay = () => {
if (isPlaying ) {
spotifyPlayer.pause();
setIsPlaying(false);
} else {
spotifyPlayer.play();
setIsPlaying(true);
}
}
