Tengo 2 componentes que representan un semáforo que muestra colores rojo y verde y están sincronizados entre sí. El tiempo de cada color del semáforo será dinámico, en función de los datos de tráfico que obtengamos pero sincronizados (si un semáforo tiene color verde durante 8 segundos, el otro debe tener color rojo durante 2 segundos (asumiendo que el total serían 10 segundos).
El siguiente código funciona, pero ¿qué pasa si cada 10 segundos (llamaré a una API) quiero cambiar sus tiempos de verde/rojo? ¿Cómo lo abordarías?
const [bottomToUp, setBottomToUp] = useState({ redLight: false, greenLight: true, greenOnTime: 6, redOnTime: 4, }); const [rightToLeft, setRightToLeft] = useState({ redLight: true, greenLight: false, greenOnTime: 4, redOnTime: 6, }); useEffect(() => { setTimeout( () => { setBottomToUp((prevState) => ({ ...prevState, redLight: !prevState.redLight, greenLight: !prevState.greenLight, })); }, bottomToUp.redLight ? bottomToUp.greenOnTime * 1000 : bottomToUp.redOnTime * 1000 ); }, [bottomToUp]); useEffect(() => { setTimeout( () => { setRightToLeft((prevState) => ({ ...prevState, redLight: !prevState.redLight, greenLight: !prevState.greenLight, })); }, rightToLeft.redLight ? rightToLeft.greenOnTime * 1000 : rightToLeft.redOnTime * 1000 ); }, [rightToLeft]); return (<div> <TrafficLight RedOn={rightToLeft.redLight} GreenOn={rightToLeft.greenLight} /> <TrafficLight RedOn={bottomToUp.redLight} GreenOn={bottomToUp.greenLight} /> </div>)Si los semáforos dependen unos de otros, eso debería reflejarse en el código. La sincronización podría hacerse un poco más efectiva sin separar el estado de los semáforos. Además de mantener un solo estado, si desea mantener las luces rojas y verdes sincronizadas, debe mantener una fija y calcular la otra a partir de la longitud total y la longitud de la luz fija.
const [bottomToUp, setBottomToUp] = useState({ greenLight: true, greenOnTime: 1, totalSemaphoreTime: 3, }) /** Using ref here because the state changes would cause the useEffect hook to reevaluate and clear the previous timeout before it was completed */ const bottomToUpRef = useRef(bottomToUp) bottomToUpRef.current = bottomToUp const fetchNewGreenOnTimeFromAPI = () => { const newGreenOnTime = getFromAPI() setBottomToUp(prev => ({ ...prev, greenOnTime: newGreenOnTime })) } useEffect(() => { let timeoutId const timeout = () => { return setTimeout( () => { setBottomToUp(prevState => ({ ...prevState, greenLight: !prevState.greenLight, timerStarted: false, })) timeoutId = timeout() }, bottomToUpRef.current.greenLight ? bottomToUpRef.current.greenOnTime * 1000 : (bottomToUpRef.current.totalSemaphoreTime - bottomToUpRef.current.greenOnTime) * 1000 /** Only using the greenLight as parameter since red light is dependent on green light */, ) } timeoutId = timeout() return () => { clearTimeout( timeoutId, ) /** You should always clear timeout when component unmounts */ } }, []) return ( <div> {/** Switched the right to left semaphore values to be reflected from the bottomToUp state since they are dependent */} <TrafficLight RedOn={bottomToUp.greenLight} GreenOn={!bottomToUp.greenLight} /> <TrafficLight RedOn={!bottomToUp.greenLight} GreenOn={bottomToUp.greenLight} /> </div> )