Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to clean up? #13

Closed
trusktr opened this issue Jan 19, 2019 · 4 comments
Closed

How to clean up? #13

trusktr opened this issue Jan 19, 2019 · 4 comments
Labels
question Further information is requested

Comments

@trusktr
Copy link
Contributor

trusktr commented Jan 19, 2019

Once we've made some elements, and things are running, how do we clean them up? Deleting the element from DOM or losing the reference doesn't cause the element to be collected in the following example:

        <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
        <script src="https://unpkg.com/solid-standalone"></script>
        <script type='text/solid'>
            const { useState, root } = Solid;

            const App = () => {
                const [state, setState] = useState({counter: 0});
                setInterval(() => setState('counter', c => c + 1), 1000);
                return <div>{( state.counter )}</div>
            }

            root(() => document.body.appendChild(<App />));
        </script>

It gets compiled to:

const _tmpl$ = document.createElement("template");

_tmpl$.innerHTML = "<div></div>";
const {
  useState,
  root
} = Solid;

const App = () => {
  const [state, setState] = useState({
    counter: 0
  });
  setInterval(() => setState('counter', c => c + 1), 1000);
  return function () {
    const _el$ = _tmpl$.content.firstChild.cloneNode(true);

    SolidDOM.r.insert(_el$, () => state.counter);
    return _el$;
  }();
};

root(() => document.body.appendChild(App()));

so the inner function created by <div>{( state.counter )}</div> for example contains a reference to _tmpl$, etc, and the counter is incremented forever even if all references to the div are lost.

How can we do the same example, but with a way to clean up when finished?

@ryansolid
Copy link
Member

Yeah you need to release the root. Often with the top level root it is unnecessary since it goes with the page.

root callback function first argument is a dispose method that you can call to cleanup all computations in that context(it's the same as S.root). You will still need to remove the element as well. I use this pattern in Solid Components per Custom Element, but in the case of the example you want call dispose when you are ready.

So something like:

const { useState, root } = Solid;

const App = () => {
    const [state, setState] = useState({counter: 0});
    setInterval(() => setState('counter', c => c + 1), 1000);
    return <div>{( state.counter )}</div>
}

let dispose;
root(disposer => {
    dispose = disposer;
    document.body.appendChild(<App />);
});

// Sometime later
dispose();

@ryansolid ryansolid added the question Further information is requested label Jan 20, 2019
@trusktr
Copy link
Contributor Author

trusktr commented Jan 21, 2019

Ah, thanks! I see the disposer mentioned in the API now, but cleanup in the examples (especially in README and the repo's .md files) seems a little sparse. It'd be great to make this more obvious from the get go.

In the todomvc example,

  • on these lines, can you explain how the useCleanup() knows when to clean up? Is there anything else that needs to be cleaned up in that example?
    • For example supposing that the whole TodoMVC app was used as a sub-component embedded in a web app that switches "applications" based on URL routing and it needs to clean up the TodoMVC "app" when unloading it.
    • I know we could use iframes, but suppose we use a single browsing context and each "app" is a component that needs to get mounted and unmounted.

In the web-components-todo example, on these lines, when would handleSubmit be removed?


So to clean up the setInterval in the above example, is the following the best way?

const { useState, useCleanup, root } = Solid;

const App = () => {
    const [state, setState] = useState({counter: 0});
    const interval = setInterval(() => setState('counter', c => c + 1), 1000);
    useCleanup(() => clearInterval(interval))
    return <div>{( state.counter )}</div>
}

let dispose;
root(disposer => {
    dispose = disposer;
    document.body.appendChild(<App />);
});

// Sometime later
dispose();

@ryansolid
Copy link
Member

Yeah I need to consider where to better describe the execution cycle. All of Solid's code involves just nesting computations like useEffect. Even root is just a computation that doesn't track dependencies. Each computation creates a context that, as well as track dependencies, registers every non-root child computation. Every time a computation is re-executed or is released itself, its child computations and dependent subscriptions are released. This is how everything stays dynamic.

useCleanup registers another method to call when the parent context (computation) is going through its cleanup. So in the case of root when dispose is called. In the case of any other effect when it is released or re-evaluated.

So yes you are correct in your example. I didn't even realize I hadn't added that. I guess because I never release in the codepen example. TodoMVC having it is likely a result of me developing it originally wrapped in a Web Component that I conditionally rendered on the page. I'm glad it's there. I probably should update the codepen example even though it isn't setup to ever release.

@ryansolid
Copy link
Member

You should check out the new Codepen example I just added for Async Effect. It really showcases the power of useCleanup.

In that example I'm using useCleanup to cancel state updates from stale promises that I have previously invoked while reactively responding to input changes to trigger async data fetching.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants