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

Svelte 5: Reactivity is not triggered when changing data #500

Open
vyconm opened this issue Dec 5, 2024 · 13 comments
Open

Svelte 5: Reactivity is not triggered when changing data #500

vyconm opened this issue Dec 5, 2024 · 13 comments

Comments

@vyconm
Copy link

vyconm commented Dec 5, 2024

[svelte] reactive_declaration_non_reactive_propertyA $: statement (node_modules/​@unovis/​svelte/​containers/​single-container/​single-container.svelte:47:0) read reactive state that was not visible to the compiler. Updates to this state will not cause the statement to re-run. The behaviour of this code will change if you migrate it to runes mode

I am not sure, if there is any workaround to this other than fully migrating unovis/svelte to svelte 5. Creating a Seperate Component to wrap unovis in, which runs in legacy mode, does not help the issue.

As the code is auto-generated according to the code comments, would that be fairly straight forward?

@lee00678
Copy link
Collaborator

lee00678 commented Dec 5, 2024

@vyconm which version of @unovis/svelte are you using?

@vyconm
Copy link
Author

vyconm commented Dec 5, 2024

@vyconm which version of @unovis/svelte are you using?

latest stable, that being 1.4.5

@lee00678
Copy link
Collaborator

lee00678 commented Dec 5, 2024

@vyconm which version of @unovis/svelte are you using?

latest stable, that being 1.4.5

We have a beta version which hopefully will fix this issue, see here: #487 (comment). Do you mind give it a try?

@vyconm
Copy link
Author

vyconm commented Dec 6, 2024

I tried the 1.5.0-svelte0 of /ts and /svelte, unfortunately didnt change the issue.

As far as I understand, this won't be resolved until the components themselves use svelte 5 syntax, which unfortunately would be breaking for svelte <= 4 users.

The Workaround for now is to wrap the component in a {#key} block with the data - not elegant, but works great.

I will try to manually rewrite the components in svelte 5 and see if that resolves it, to test my theory.

@vyconm
Copy link
Author

vyconm commented Dec 6, 2024

I may very well be too stupid to write node-modules, somehow I get the same error even when forcing runes mode, which seems nonsensical. I quickly ported the single-container.svelte to this:

<svelte:options runes={true} />

<script>
  import { SingleContainer } from "@unovis/ts";
  import { arePropsEqual } from "../../utils/props";
  import { onDestroy, setContext } from "svelte";

  // State
  let chart = $state();
  let ref = $state();
  let component = $state();
  let tooltip = $state();
  let annotations = $state();
  let shouldUpdate = $state(false);
  let prevConfig = $state();

  // Props
  let { data, class: className = "", children, ...restProps } = $props();

  // Derived values
  let config = $derived({
    component,
    tooltip,
    annotations,
    ...restProps,
  });

  // Helpers
  function initChart() {
    chart = new SingleContainer(ref, config, data);
  }

  function updateChart(forceUpdate = false) {
    if (forceUpdate) {
      chart?.update(config, null, data);
    } else if (shouldUpdate) {
      chart?.updateContainer(config);
    }
    shouldUpdate = false;
  }

  // Effects
  $effect(() => {
    chart?.setData(data);
  });

  $effect(() => {
    shouldUpdate = Object.keys(restProps).some(
      (k) => !arePropsEqual(chart?.config[k], restProps[k]),
    );
  });

  $effect(() => {
    if (shouldUpdate) {
      updateChart();
    }
  });

  $effect(() => {
    if (component) {
      !chart ? initChart() : updateChart(true);
    }
  });

  // Context
  setContext("tooltip", {
    update: (t) => {
      tooltip = t;
    },
    destroy: () => {
      tooltip = undefined;
    },
  });

  setContext("component", {
    update: (c) => {
      component = c;
    },
    destroy: () => {
      component = undefined;
    },
  });

  setContext("annotations", {
    update: (a) => {
      annotations = a;
    },
    destroy: () => {
      annotations = undefined;
    },
  });

  onDestroy(() => {
    if (chart) {
      chart.destroy();
    }
  });
</script>

<vis-single-container
  bind:this={ref}
  class={`unovis-single-container ${className}`}
>
  {#if children}
    {@render children()}
  {/if}
</vis-single-container>

<style>
  .unovis-single-container {
    display: block;
    position: relative;
    width: 100%;
  }
</style>

svelte] reactive_declaration_non_reactive_propertyA$:statement (node_modules/​@unovis/​svelte/​containers/​single-container/​single-container.svelte:47:0) read reactive state that was not visible to the compiler. Updates to this state will not cause the statement to re-run. The behaviour of this code will change if you migrate it to runes mode

The error for some reason stays the same, even when running dev --force. I might be out of my comfort zone here, hope I can still provide some insight.

@lee00678
Copy link
Collaborator

lee00678 commented Dec 9, 2024

@vyconm Do you think you can provide a StackBlitz example showing the issue you've mentioned? @pingu-codes maybe you can provide some insights on this?

@vyconm
Copy link
Author

vyconm commented Dec 9, 2024

@vyconm Do you think you can provide a StackBlitz example showing the issue you've mentioned? @pingu-codes maybe you can provide some insights on this?

I'll try tomorrow!

@lee00678
Copy link
Collaborator

I don't have much experience with Svelte, but I just made 2 examples, Svelte 4 and Svelte 5. Doesn't seem like the data is updating in V5. @pingu-codes any thoughts?

@vyconm
Copy link
Author

vyconm commented Dec 10, 2024

I don't have much experience with Svelte, but I just made 2 examples, Svelte 4 and Svelte 5. Doesn't seem like the data is updating in V5. @pingu-codes any thoughts?

Svelte 5 changed how reactivity works, using Signals (called runes) like in SolidJs.

Nevertheless, svelte promises full backwards compatability so even your svelte 5 example with old syntax is supposed to work. Weirdly it still wants updated Syntax for unovis, which is confusing.

@pingu-codes
Copy link
Contributor

This is a really hard one to debug. I believe what's happening is svelte 5 is actually treating the reactivity correctly where as svelte 4 was rerendering the parent xy container with updateContainer when the child component was updated or data was changed regardless of wether it should actually trigger updateContainer

Here's an image highlighting the reactive statement within the compiled svelte 4 update code which is responsible for the reactivity which works in svelte 4:
image

Svelte 5 seems to be more correct in that it only calls updateContainer when config or $$restprops are update:
image

@pingu-codes
Copy link
Contributor

pingu-codes commented Dec 10, 2024

I made a couple of changes which seem to work for the line component:

        // variable to store the previous data
        let prevData: Datum[] = undefined;
        onMount(() => {
		// ...
                // data must be set for the initial render to work
		component.setData(data);
		// ...
	});
        // this is disabled
        // $: component?.setData(data);
        let animationFrame = 0;
	const triggerReRender = () => {
		component?.setData(data);
		if (typeof requestAnimationFrame === 'undefined') {
			component?._render();
			return;
		}

		if (animationFrame) {
			cancelAnimationFrame(animationFrame);
		}

		animationFrame = requestAnimationFrame(() => {
			component?._render();
			animationFrame = 0;
		});
	};

        // if the data has been changed the component will have the data set AND be re-rendered with ._render()
	$: if (prevData !== data || (data !== undefined && prevData === undefined)) {
		prevData = data;
		triggerReRender();
	}

@lee00678
Copy link
Collaborator

@pingu-codes did you get a chance to look at the Svelte 5 example I made? If Svelte 5 is backwards compatible, why isn't the updating button working?

@pingu-codes
Copy link
Contributor

Hey @lee00678, I just took a look. The update button is working and the array is being updated if you console.log it. The chart isn't being updated due to the problems with svelte 5 and unovis reactivity when data changes 😔

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

No branches or pull requests

3 participants