---
# System prepended metadata

title: Brainstorming of `Closures`

---

# Brainstorming of `Closures`

## Problem how to update the Counter

```typescript=
import {serverAdd} from './logic';

const Counter = qComponent({
  onMount: qHook(() => {
    return {
      count: await serverAdd(props.start, 1)
    }
  }),
  onRender: qHook((props) => (
    <>
      <span>{state.count}</span>
      <button on:click={qHook(() => {
        state.count = await serverAdd(state.count, 1)
      })}>+</button>
    </>
  ))
})
```

Problems:
- `serverAdd` function must be extracted, which imposes extra mental load on the developer.

## `useEffect`

```typescript=
import {serverAdd} from './logic';

const CounterEffect = qEffect<{recompute: number}, {value: number}>({...});

const Counter = qComponent<{}, {counterEffect: typeof CounterEffect}>({
  onMount: qHook(() => {
    const state = useState({counter: 0, dirty: number})
    state.counterEffect = useEffect(ServerCounter, state, {recompute: 'dirty'});
    return state;
  }),
  onRender: qHook((props) => (
    <>
      <span>{state.counterEffect.value}</span>
      <button on:click={qHook(() => {
        // Invalidate the effect, which will cause a reCompute => onRender.
        state.dirty++;
      })}>+</button>
    </>
  ))
})
```




## Unified qHook() semantics
Logic within a qHook() should work the same regardess of its context

- Right now, onMount qhooks have a lightly different semantic regarding the return value and the props


----

```typescript=
import {serverAdd} from './logic';

const CounterOnMount:OnMount<typeof Counter> = () => {
    return {
      count: await serverAdd(props.start, 1)
    }
  };


const CounterOnRender:OnRender<typeof Counter> = (props, state) => (
    <>
      <span>{state.count}</span>
      <button on:click={(props: PropsOf<typeof Counter>, state: StateOf<typeof Counter>, args: {value: number}) => {
        state.count = await serverAdd(state.count, args.value)
      }}>+</button>
      <button on:click={(props, state) => {
        state.count = await serverAdd(state.count, 1)
      } as OnEvent<typeof Counter>}>+</button>
      <button on:click={qHook<typeof Counter>((props, state) => {
        state.count = await serverAdd(state.count, 1)
      })}>+</button>

      <button on:click={() => {
        state.count = await serverAdd(state.count, 1)
      }>+</button>
    </>
  );

const Counter = qComponent({
  onMount: CounterOnMount,
  onRender: CounterOnRender,
})
```

```typescript=
// Option: A
const Counter = qComponent<{value: number}, {count: number}>({
  onMount: (props) => ({count: props.value}),
  onRender: (props, state) => (
    <>
      <span>{state.count}</span>
      <button on:click={() => state.count++}>+</button>
    </>
  )
})

// Option: B
const Counter = qComponent(function(props: {value: number}, state: {count: number}) {
  onMount(() => ({count: props.value}));
  onRender(() => (
    <>
      <span>{state.count}</span>
      <button on:click={() => state.count++}>+</button>
    </>
  ));
});



// Option: C
// Core hooks
- useState()
-_saveState()
- on(event, fn) => 
  - onResume => on('qResume', fn);
  - onUnmount => on('qUnmount', fn);

- useAnyWatch() // Rerun when any state changes (fine for Render())
- useSmartWatch() // Rerun when the watched state changes (what useEffect() needs )

- onRender()

- useEffect()
- useMemo() // computed value



function useMemo(computeFn, args) {
    const state = useState({value: undefined});
    
    useWatch(() => {
        state.foo
    })
    useEffect(() => {
        state.value = computeFn(..args);
    }, args)
    return state:
}

function useCounter = () => {
  const state = useState({count: 0});
  let intervalId;
  onResume(() => {
    intervalId = setInterval(() => {
      state.count++;
    }, 1000);
  });
  onUnmount(() => {
    intervalId && intervalId = clearInterval(intervalId);
  }); 
  return state;
}

function useCounterV2 = () => {
  const state = useState({count: 0});
  let intervalId;
  onResume(() => {
    intervalId = setInterval(() => {
      state.count++;
    }, 1000);
  });
  onUnmount(() => {
    intervalId && intervalId = clearInterval(intervalId);
  }); 
  return state;
}


const Counter = qComponent(function({value}: {value: number, increment: number}) {
  const state = useState({count: props.value});
  const foo = foo() // using useState() internally;
  const state = useState({count: 0});
  const bar = useCounter();
    
  
  
  // Issue what if we call helper function `foo` to invoke onRender?
  // (We could look at stacktrace and dissallow it.)
  onRender(() => (
    <>
      <span>{state.count}{props.value}</span>
      {buttons.map((btn, index) => (
        <button on:click={() => state.count += index}>{index}</button>
        <button on:click={((i) => state.count += i}).bind(i)>{index}</button>
        <button {...on('click', index, (idx) => state.count+= idx)}>{index}</button>
      ))}
    </>
  ));
});

// After optimizer.
const Counter = qComponent({
  onMount: qHook(() => {
    const state = useState({count: props.value});
    const foo = foo();

    // Compiler generates:
    _qSaveState(':state', state);
    _qSaveState(':foo', foo);

    const bar = useEffect(() => {
      async function doStuff() {
          await fetch()
      };
      const state = _useState(':state');
      const foo = _useState(':foo');
      doStuff(state.count + foo.value);
  });
          _qSaveState(':bar', bar);

  }),
  onRender: qHook(() => {
    const props = _useProps();
    const state = _useState(':state');
    const foo = _useState(':foo');
    return (
      <>
        <span>{state.count}{props.value}</span>
        <button on:click={qHook(() => {
          const state = useState(':state');
          state.count++;
        }>+</button>
      </>
    );
  });
})



// After optimizer v2
const Counter = qComponent({
  onMount: qHook(() => {
    const state = useState({count: props.value});
    const foo = foo();

    // Compiler generates:
    _qSaveState(':state', state);
    _qSaveState(':foo', foo);

    const bar = useEffect(() => {
      async function doStuff() {
          await fetch()
      };
      const state = _useState(':state');
      const foo = _useState(':foo');
      doStuff(state.count + foo.value);
  });
          _qSaveState(':bar', bar);

  }),
  onRender: qHook((props, state, foo) => {
    return (
      <>
        <span>{state.count}{props.value}</span>
        {buttons.map((btn, index) => (
              <button on:click={qHook((state, index) => state.count += index}).with(state, index)>{index}</button>
        ))}
        <button on:click={qHook(() => {
          const state = useState(':state');
          state.count++;
        }>+</button>
      </>
    );
  }).with(':s:state', ':s:foo', ':p')
})


// Option: D
const Counter = qComponent(function(props: {value: number, increment: number}) {
  // Issue what if we call helper function `foo` to create state?
  // (We could look at stacktrace and dissallow it.)
  const state = useState({count: props.value});
  const fooState = foo();
  
  // Issue what if we call helper function `foo` to invoke onRender?
  // (We could look at stacktrace and dissallow it.)
  useRender(qHooo() => (
    <>
      <span>{state.count}</span>
      <button on:click={click(state, () => state.count++)}>+</button>
      <button on:click={() => {
        fooState.count++;
      }>+</button>
      <button on:click={with(
          props.increment, 
          (increment: number) => {
                        const state = useState(0);
                        state.count += increment
                       })}>+</button>
    </>
  ));
});
```

---

```typescript=
const Counter = qComponent((props: {}) =>{
  const state = useState({count: 0});
  
  onRender(() => (
    <>
      <span>{state.count}</span>
      {btns.map((btn) => (
        <button on:click={() => state.count += btn.delta}>{btn.name}</button>
      ))}
    </>
  ));
});
```

```typescript=
const CounterOnRender = (state) => {
  return (
      <>
        <span>{state.count}</span>
        {btns.map((btn) => (
          <button on:click={qHook("./single#onClick", "onClick", [state, btn])}>
            {btn.name}
          </button>
        ))}
      </>
    );
  }

const onClick = (state, btn) => {
  state.count += btn.delta;
});

const Counter = qComponent(function (props){
  let state = useState({count: 0});
  onRender(qHook("./single#CounterOnRender", "CounterOnRender", [state]));
});
```