# Brainstorming: Use Effect ```typescript= import {invalidate, useState} from '@builder.io/qwik'; interface WeatherState { temp: number; } interface WeatherProps { city: string; } function fetchWeather(props: WeatherProps, state: WeatherState) { const data = await(await(fetch(`.../${props.city}`)).json()); state.temp = data.temp; } function IntervalUpdate(this: WeatherState, props: WeatherProps): number { let intervalId = setInterval(fetchWeather, 1000, props, this); return [intervalId, () => clearInterval(intervalId)]; } function useWeather(props: WeatherProps) { const state = useState<WeatherState>({temp: null}); onResume(() => useTransient(state, IntervalUpdate, props)); onHalt(() => releaseTransient(state, IntervalUpdate)); useWatcher(() => fetchWeather(props, state), [props]); return state; } const Weather = qComponent((props: {city: string}) => { const weatherProps = useState({city: props.city}); const weather = useWeather(weatherProps); return () => ( <div> <span>{weatherProps.city}</span> <span>{weather.temp}</span> <button on:click={() => invalidate(weatherProps) }>refresh</button> </div> ); }); ``` ```typescript= export const x = (props: {city: string}) => { const weatherProps = useState({city: props.city}); const weather = useWeather(weatherProps); onRender( () => ( <div> <span>{weatherProps.city}</span> <span>{weather.temp}</span> <button on:click={() => invalidate(weatherProps) }>refresh</button> </div> )); } const Weather = qComponent(x); // const Weather = qComponent(qHook('./chunk#x')); function genericRendering(x) { return onRender(() => <span>{x}</span>) // const export s1 = () => <span>{x}</span>; // return onRender(qHook('./chunk#s1')) } const MyComp = qComponent(() => { return genericRendering('abc') }); ``` ```typescript= const MyComp = qComponent(async (proprs: {}) => { const result = await HUGE_LIBRARY_NODE(); const state = useState(result.value); onRender(() => <span>{state}</span>); return () => <span>{state}</span>; }) ``` transform to: ```typescript= const s1 = () => { const [state] = useClosure(); return <span>...</span>; } const s2 = () => { const [state] = useClosure(); return <span>...</span>; } const s3 = (proprs: {}) => { const result = await HUGE_LIBRARY_NODE(); const state = useState(result.value); onRender(qHook(`./chunk#s1`, [state])); // <== consider not supporting return qHook(`./chunk#s2`, [state]); // <=== Preffered } const MyComp = qComponent(qHook('./chunk#s3')); const Weather = qComponent((props: {}) => { onMount: () => { const weatherProps = useState({city: props.city}); const weather = useWeather(weatherProps); } onRender: () => { <div> <span>{weatherProps.city}</span> <span>{weather.temp}</span> <button on:click={() => invalidate(weatherProps) }>refresh</button> </div> } onRender(() => ( <div> <span>{weatherProps.city}</span> <span>{weather.temp}</span> <button on:click={() => invalidate(weatherProps) }>refresh</button> </div> )); }); ``` ## Clock use case: ```typescript= import {useInterval} from '@builder.io/qwik'; export Clock = qComponent((props: {}) => { const time = useInterval(qFn(() => ({value: new Date()}), 1000)); onRender(() => <span>{time.value}</span>); }); ``` # `useInterval` ```typescript= import {cleanupTuple, useState} from '@builder.io/qwik'; export function IntervalUpdate(fn: QFunction, returnValue: {}, delay: number) { let intervalId = setInterval(() => assign(returnValue, await fn.invoke()), delay); return cleanupTuple([intervalId, () => clearInterval(intervalId)]); } export function useInterval(fn: QFunction, delay: number) { const state = useState(); onResume(() => useTransient(state, IntervalUpdate, fn, state, delay)); onHalt(() => releaseTransient(state, IntervalUpdate)); return state; } ``` ## Refresh use-case: ```typescript= export MyCmp = qComponent((props: {a, b}) => { const state = useState({ value: 0}); useEffect((a, b) => { state.value = a + b; }, [props.a, props.b]); onRender(() => <span>{stock}</span>) }); ``` Problem: - By listing dependencies explicitly there is no way to rerun the reading of the deps. We don't know that we will have to re-run when `props.a` or `props.b` changes. ```typescript= export MyCmp = qComponent((props: {a, b}) => { const state = useState({ value: 0}); const c = foo(); useEffect(() => state.value = props.a + props.b + c.bar); useEffect(($) => state.value = $(props).a + $(props).b + $(c).bar); // export const effectSymbol = () => { // const [state] = getClosure(); // return state.value = props.a + props.b; // } // useEffect(qHook('.chunk#effectSymbol{*state}|*props.a.b')) onRender(($) => (<span>{$(stock).price}</span>)); }); ``` Problem: - By not listing dependencies explicitly we can use proxy to figure out what we need to listen on. But the `useEffect` function must be synchronous as we will not be able to tell async reads with which `useEffect` they go with! - As of right now the tracking system is object level only (not prop level). Do we care? ```htmlembedded= <div q:render="./path#renderSymbol{*state}|*state" q:effect="./path#effectSymbol{*state}|*props.propA.propB,*otherState.propC" q:obj="abc xyz" > </div> ``` ```typescript= export function useMemo(fn: QFunction, deps: QSerializable[]): QObject { let value; useEffect((fn) => value = await fn.invoke(), fn); return value; } ``` ```typescript= export const MyComp = qComponent(() => { const state = useState({count:0}) const interval = useInterval(1000); useEffect( (timestamp: number) => state.count++, [propOf(interval, 'timestamp')] ); // q:effect="./chunk#symbol{state}|interval.timestamp" onRender(() => <span>{state}</span>) }) ``` --- # Manu's `useInterval` ```typescript= import {cleanupTuple} from '@builder.io/qwik'; export function IntervalUpdate(fn: QFunction, delay: number) { let intervalId = setInterval(() => await fn.invoke(), delay); return cleanupTuple([intervalId, () => clearInterval(intervalId)]); } document.load => whatever function idealUseInterval(fn, delay) { useEffect(() => { let thing = setInterval(() => fn(), delay); return () => { clearInterval(thing); } }, [fn, delay]); } export function useInterval(fn: QFunction, delay: number) { const value = useState({count: 0}); onResume(() => useTransient(value, IntervalUpdate, fn, delay)); onHalt(() => releaseTransient(value, IntervalUpdate)); return value; } ``` ## Clock use case 2 ```typescript= import {useInterval} from '@builder.io/qwik'; export function useClockWithFunction = (fn, tickTime) => { const state = useState({count: 0}); onResume(() => { useEffect(() => { const timer = setInterval(()=>{ fn(); }, tickTime); return () => clearInterval(timer); }); }) } export ClockImplemention = (props: {}) => { useClockWithFunction // 0, Unserializable onMount(() => { setTimeout() }); }; import css from './cmp.css' export Clock = qComponent((props: {}) => { withTag('bar'); return <Foo>{state}</Foo>); }); /** @qwik */ function Foo() { return <button></button> } function genericRender(state) { return () => { } } ``` --- ```typescript= export const x = () => { return foo(); }; export MyComp = qComponent(x) export foo() { return () => <span></span> } ``` - using onRender() - qComponent takes an arrow function ```typescript= import {expr, expr2} from './bar' MARKER_FN(expr()) // export const s1 = expr() // MARKER_FN(qHook('./chunk/s1')) <div on:___={expr2}> // export cnst s2 = expr2 // <div on:__={qHook('./chunk#s2')} ```