solidjs / solid-router

A universal router for Solid inspired by Ember and React Router
MIT License
1.14k stars 147 forks source link

Data Function behaves differently in Soft vs Hard navigation #308

Closed atilafassina closed 10 months ago

atilafassina commented 11 months ago

Describe the bug

It seems there's no way to trigger the Data Function of a dynamic route when navigating from a sibling. So the same route will have different data if reached via hard navigation vs soft navigation.

Using the <Show keyed ...> workaround re-renders the route, but data that depends on the route's data function remains stale.

deployed example: https://solid-router-test.vercel.app

Your Example Website or App

https://github.com/atilafassina/solid-router-test/blob/main/src/entry.jsx

Steps to Reproduce the Bug or Issue

on this app: https://solid-router-test.vercel.app/

  1. Check Soft navigation to see the component re-renders with stale data.
  2. Check Hard Navigation to see that the component re-renders with fresh data.

(the h1 content needs to match route param).

Expected behavior

Data function still gets triggered if route params change. Causing data to be updated even on soft navigation.

Screenshots or Videos

No response

Platform

pkg version
@solidjs/router ^0.9.1
solid-js ^1.7.8

Additional context

No response

ryansolid commented 10 months ago

This is by design. You find this commonly in all fine grained libraries. Things like Vue as well. We don't want to always re-render the page and if we took that stance that we did, there would be no way to avoid it.

The data would update if it is reactively accessed/passed. This whole example uses data functions incorrectly:

  1. Return the Signal not the value as it won't run again: https://github.com/atilafassina/solid-router-test/blob/main/src/entry.jsx#L71C11-L71C11 This line should be: return payload Calling the function accesses it before it gets a value because it is async.

  2. Access reactive signals in the reactive part of createResource. The fetcher is intentionally inert. It was a necessary part of the current createResource design for hydration(although I've figured a way around it for the future). So https://github.com/atilafassina/solid-router-test/blob/main/src/entry.jsx#L63-L77 Should look like this:

    data: ({ params, data }) => {
          /**
           * because of my edit above it is a function
           * but the return is undefined because it initially is
           * if you wanted to see it `createEffect` and log it only when it has data
           */
          console.info("data from parent route", data());
    
          // single argument createResource isn't reactive, first argument of multi is the source to pass to query
          const [payload] = createResource(() => ({
            current: params.id,
            link: params.id === "22" ? "23" : "22",
          }), (v) => doSomething(v));
    
          return payload; // return signal
        },

    I should add like in this last case there is no need for createResource as nothing is async. All of these could just be createMemo. Basically these functions let you create whatever primitives you want and return anything you want.

In anycase with these changes it will update as expected. Ironically this API is gone in the new version of the router so I guess it doesn't matter all that much.

atilafassina commented 10 months ago

Thank you for the thorough response, @ryansolid Very very useful and got my head in the right place about createResource yet again :)

I should add like in this last case there is no need for createResource as nothing is async

Yes, this example doesn't make justice to my actual use-case. I actually have a gRPC call that changes based on the route params. What I end up doing is move the createResource within the component instead of the DataFunction. But I'll give your proposal a try, at least until the new version comes up.

In anycase with these changes it will update as expected. Ironically this API is gone in the new version of the router so I guess it doesn't matter all that much.

Yeah, for the future it doesn't. But I'm about to make a livestream refactoring an app from Solid-Router 0.9 to 0.10. So having bit as it was supposed to be at 0.9 will be useful to answering questions on 0.10.

So thanks again!! I'll definitely make use of that information :)