Skip to content

[Bug]:@use-funnel/browser is not being processed on the server side. #59

Closed
@XionWCFM

Description

@XionWCFM

Package Scope

@use-funnel/browser

Bug description

I think this is a bug.

@use-funnel's docs recommend using @use-funnel/browser when using the next.js approuter, but

In fact, if you use @use-funnel/browser in the app router, the project cannot be built because server-side processing is not performed properly.

This issue can also be found in @use-funnel/browser@0.0.5 version.

The reason this problem occurs is that using the 'use client' directive when using the next.js app router does not mean that the file will never be evaluated on the server side.

In fact, next.js also analyzes files containing the use client directive during the build process, and if there is code that accesses the window object, an error occurs.

If we want to pass the use case for app router to @use-funnel/browser, I think we will have to do server-side processing.

Expected behavior

We want error-free builds.

To Reproduce

@use-funnel/browser 0.0.5
next 14.2.13

Possible Solution

I made a few modifications to this code base locally to resolve this issue.

Simply put, the problem of errors occurring during build can be solved by modifying the code so that it does not directly access the window object.

Because this is exactly the code that is causing the problem.

export const useFunnel = createUseFunnel(({ id, initialState }) => {
  const [location, setLocation] = useState(() => ({
    search: window.location.search,
  }));
  const [state, setState] = useState(() => ({
    ...window.history.state,
  }));

This code raises an error in an environment where the window object does not exist.

So, you can modify it like this:

  const [location, setLocation] = useState({ search: '' });
  const [state, setState] = useState<any>({});

  useEffect(() => {
    if (typeof window !== 'undefined') {
      setLocation({ search: window.location?.search ?? '' });
      setState(window.history?.state ?? {});

      const handlePopState = (event: PopStateEvent) => {
        setLocation({ search: window.location?.search ?? '' });
        setState(event?.state ?? {});
      };

      window.addEventListener('popstate', handlePopState);
      return () => {
        window.removeEventListener('popstate', handlePopState);
      };
    }
  }, []);

This code is buildable because it does not access the window object on the server side and only reflects the state through useEffect on the client side, and can also avoid the Text content does not match server-rendered HTML error.

However, there are user experience issues with this approach.

Since the initial value of the server is first rendered and updated according to the current position through useEffect, if it is not initialStep, initiailStep is shown first and then the UI blinks as the desired step is rendered.

What's even more disappointing is that this approach has the potential to affect use cases that don't use app-router.

So I think there are two options.

  1. Create a new adapter for app-router, place a dependency on next, and create a package that uses hooks provided by next.js, such as useSearchParams.

  2. Modify the @use-funnel/browser package to take server side into consideration.

In my opinion, if there is a way to solve the problem of UI flickering when @use-funnel/browser properly supports the server-side environment while also properly supporting the server-side environment, that would be the best method.

Alternatively, creating an adapter for app-router seems attractive.

Do you have any ideas?

etc.

Or there is another way: Using dynamic in next.js ensures that it will always run only on the client, thus solving UI flickering issues and build issues.

However, this method requires more knowledge from the user.

import dynamic from "next/dynamic";
const Example = dynamic(() => import('../src/funnel'))

thank you!

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions