joshnuss/react-hooks-in-svelte

Add example of useEffect with dependencies

Closed this issue · 6 comments

Great work with this. Really interesting way for me to learn more about Svelte.

For the useEffect example, I saw you are only showing a replacement for useEffect with no dependencies for the "onMount" case.

Would be interested in seeing an example for #3 in the readme "With a list of dependency vars: useEffect(fn, [a, b, c]). This reavaulates whenever a dependency changes. Cleans up last value if needed."

Totally agree. I will look into it when I have some more time.

Thanks for opening the issue!

A useEffect implementation with param can implemented like that
https://dylanvann.com/svelte-version-of-useeffect

There was a discussion about this on twitter, so dropping link here:
https://twitter.com/RyanCarniato/status/1329955721375404032

@hmillison @joshnuss @ctjhoa

In Svelte we don't need useEffect at all. Expression with $: sign check all dependencies automatically and run only when that dependencies changed. Dependencies here is just variables inside $: expression. That's why svelte is so awesome

React example

function test() {
  const [firstname, setFirstname] = useState('foo')
  const [lastname, setLastname] = useState('bar')
  const [somethingElse, setSomethingElse] = useState('somethingElse')

  useEffect(() => {
    console.log(`runs only when firstname: ${firstname} or lastname: ${lastname} changed.`)
  }, [firstname, lastname])

  return (
    <>
      <input
        type="text"
        onInput={(e) => setFirstname(e.target.value)}
        placeholder="First name"
      />
      <input
        type="text"
        onInput={(e) => setLastname(e.target.value)}
        placeholder="Last name"
      />
      <input
        type="text"
        onInput={(e) => setSomethingElse(e.target.value)}
        placeholder="Something else"
      />
    </>
  )
}

And Svelte example check here. Try to edit text in first and second input and you will see message in the console. But if you try to edit text in third input, you will see nothing in the console because variable binded to third input have not used in the $: expression

<script>
  let firstname = 'foo'
  let lastname = 'bar'
  let somethingElse = 'somethingElse'
  
  $: {
    console.log(`runs only when firstname: ${firstname} or lastname: ${lastname} changed. Because they are used inside this expression`)
  }
</script>

<input bind:value={firstname} />
<input bind:value={lastname} />
<input bind:value={somethingElse} />

@osadasami right, if there is no cleanup, then it's just a reactive statement.

if there is cleanup, it could be managed manually

// some external resource
let resource

$: {
 // cleanup
 if (resource) resource.release()

 // subscriber using dependencies
 resource = api.subscribe(dep1, dep2, ...)
}

// cleanup when component dismounts
onDestroy(() => { if (resource) resource.cleanup() })

Another alternative is to use a Store to manage access to the resource.

I think the Svelte way is to avoid resource cleanup at the component level, and instead use stores to turn the resource into reactive state.

Here's an example of a store that wraps an external API, and runs cleanup code when necessary:

import { readable } from 'svelte/store'
import api from './external-resource'

export function resourceStore() {
  return readable(null, set => {
    const resource  = api.getResource()
  
    set(resource.value)
 
    // wire up API
    resource.subscribe(value => set(value))

    // return a cleanup function
    return () => resource.release()
  })
}