Search code examples
sveltesveltekitsvelte-3

Initialise prop with value passed from Parent component but map to reactive variable/value from store later on


In Parent.svelte:

<Child {initialName}/>

In Child.svelte

export let initialName;
<p>{initialName}</p>

This works fine, initialName is rendered from value passed from Parent. But I'd like to map it to a reactive variable from store later on ( once the initial comp is rendered).

import {_name} from './store.js';

$: initialName = $_name;

wouldn't work as initialName would be immediately overwritten by value from store. Currently I'm doing below workaround.

let initialized = false;
let l_name;
$: if (true || $_name) {
    if (initialized) {
        l_name = $_name;
    } else {
        l_name = initialName;
        initialized = true;
    }
}

<p>{l_name}</p>

this works, but I find it bit hacky and too much boilerplate. Any other clean way to accomplish the same? One option is to set value directly to store (in Parent comp) and use it <p>$_name</p>. But this had a problem of retaining the old value from store when I refresh the page for a split second.


Solution

  • You simply setup 2 individual reactive statements:

    $: name = $_name;        // `$_name` mutation will write to `name`
    $: name = initialName;   // `initialName` will override `name`, if it also mutates
    

    OR, if you only want initialName to override on first mount, but not on subsequent update, use onMount(callback) instead of reactive statement:

    $: name = $_name;       // `$_name` mutation is always sync'ed to `name`
    onMount(() => {
        name = initialName; // but `initialName` only writes once on mount
    })
    

    Sample code, see for yourself.

    <!-- Child.svelte -->
    
    <script context="module">
        import { writable, derived } from 'svelte/store'
        export const _name = writable("zoo")
    </script>
    
    <script>    
        export let initialName;
        $: name = $_name;  
        $: name = initialName;
    </script>
    
    <p>{name}</p>
    
    <!-- Parent.svelte -->
    
    <script>
        import Child, { _name } from './Child.svelte'
        let initialName = "foobar"
    </script>
    
    <Child {initialName} />
    
    <label>initialName: <input bind:value={initialName} /></label>
    <label>store value: <input bind:value={$_name} /></label>