function SchrödingersDiff() { const [count, setCount] = useState(0); // simulate a changing state useEffect(() => { const int = window.setInterval(() => setCount(s => s + 1), 1000); return () => window.clearInterval(int); }, []); const last = useRef(0); const diff = useMemo(() => { const lastValue = last.current; last.current = count; const diff = count - lastValue; // console.log(diff); return diff; }, [count]); // console.log(diff); return diff; } ¿Por qué este componente siempre representa "0" cuando se ejecuta bajo <React.StrictMode> ? Funciona bien (es decir, representa "1") sin modo estricto.
Sé que el modo estricto representa las cosas dos veces, lo que lo explicaría, pero esperaría que useMemo lo detecte, y lo hace: el inicio de sesión en la función memo imprime el "1" correcto cada segundo. Incluso el de afuera lo hace.
¿Hay una mejor manera de calcular esa diferencia?
Este es mi useState / useEffect / setInterval , ya adaptado para mantener el último:
export function useChannel(channelName, initialData, currentOnly) { const socket = useSc(s => s.socket); // gets a SocketCluster socket const [data, setData] = useState(currentOnly ? initialData : {current: initialData}); useEffect(() => { if (!socket) return; const channel = socket.subscribe(channelName); (async () => { for await (const data of channel) setData(currentOnly ? data : s => ({current: data, last: s.current})); })(); return () => channel.unsubscribe(); }, [socket, channelName]); return data; }¿Hay una mejor manera de calcular esa diferencia?
useState el enlace useState y pondría la lógica de cálculo en la función setState . Ya lo hiciste, pero podemos hacerlo un poco más genérico y reutilizable:
export function useCurrentChannel(channelName, initialData) { const [data, setData] = useState(initialData) useChannelSubscription(channelName, setData); return data; } export function useChannelSubscription(channelName, onNewData) { const socket = useSc(s => s.socket); useEffect(() => { if (!socket) return; const channel = socket.subscribe(channelName); (async () => { for await (const data of channel) { onNewData(data); } })(); return () => channel.unsubscribe(); }, [socket, channelName, onNewData]); // or put `onNewData` in a Ref return socket; }Ahora también puedes escribir
export function useHistoricChannel(channelName, initialData) { const [data, setData] = useState({current: initialData}) const handleNewData = useCallback( (data) => setData(s => ({current: data, last: s.current})), [setData] ); useChannelSubscription(channelName, handleNewData); return data; });que podría ser mejor con un reductor:
export function useHistoricChannel(channelName, initialData) { const [data, handleNewData] = useReducer( (s, data) => ({current: data, last: s.current}), {current: initialData} ); useChannelSubscription(channelName, handleNewData); return data; }); Por supuesto, puede hacer que el reductor calcule cualquier cosa (como una diferencia entre current y last ), sin tener que cambiar useChannelSubscription .
Puede definir un enlace personalizado useDiff que internamente mantiene un estado con los valores actuales y últimos para que pueda actualizar ambos desde su devolución de llamada setInterval de esta manera:
const useDiff = () => { const [state, setState] = useState({ current: 0, last: 0, }); useEffect(() => { const interval = window.setInterval(() => { setState((s) => ({ current: s.current + 1, last: s.current })); }, 1000); return () => window.clearInterval(interval); }, []); return state.current - state.last; };Demostración: https://stackblitz.com/edit/react-akituv