gatsby-packages icon indicating copy to clipboard operation
gatsby-packages copied to clipboard

`gatsby-plugin-use-query-params` not working with Gatsby V4

Open ChrisCrossCrash opened this issue 2 years ago • 30 comments

After following the migration guide and updating from Gatsby V3 to V4, I'm having problems with useQueryParams() in my project. I suspect it has something to do with how all of my other gatsby-plugin-* packages needed to be updated for V4, but an update wasn't available for gatsby-plugin-use-query-params.

Do you think this is the cause of the problem?

If you would like, I'll try to make a new repository to reproduce the issue.

ChrisCrossCrash avatar Oct 21 '21 20:10 ChrisCrossCrash

Same here. Using any setter with "replaceIn" causes my Gatsby site to render a 404.

justinmahar avatar Nov 05 '21 13:11 justinmahar

Looks like at the heart of the plugin it's wrapping the root element with a QueryParamProvider from the use-query-params package. Without it, use-query-params complains that it needs to consume that context.

import React from "react";
import { Location, globalHistory } from "@reach/router";
import { QueryParamProvider } from "use-query-params";

export default ({ children }) => (
  <Location>
    {({ location }) => (
      <QueryParamProvider location={location} reachHistory={globalHistory}>
        {children}
      </QueryParamProvider>
    )}
  </Location>
);

Somehow this isn't sufficient anymore.

justinmahar avatar Nov 05 '21 13:11 justinmahar

Workaround 💪

UPDATE: You can try the Replacement Hook Solution below which makes this plugin obsolete.


I realized this had something to do with Reach router detecting the URL changes and that there are a few ways we can tell Gatsby to ignore certain paths.

Limitations

The limitation to this workaround is that you'll need to configure the paths to the pages that set query params. It's not a fix for every page. Pages that only get query params (no set calls) are unaffected by the bug.

However this workaround can get you up and running until there's an official fix if your use case is that you only need to set query params on a handful of pages.

Workaround # 1

Use gatsby-plugin-create-client-paths to tell Gatsby's root router to ignore the query params when they change. This is equivalent to setting your page up to have a client router, except we won't actually nest a router.

  • npm install gatsby-plugin-create-client-paths
  • In gatsby-config, add the following plugin:
      {
        // Add paths to pages that set query params here.
        // This is a workaround: https://github.com/alexluong/gatsby-packages/issues/41
        resolve: `gatsby-plugin-create-client-paths`,
        options: { prefixes: [`/path-to-page-needing-query-params/*`] },
      },
    
  • Replace path-to-page-needing-query-params with the path to the page that needs to set query params. You can add as many path prefixes to that array as you'd like. You'll need one for each page that sets query params.

Using this approach, you can specify any page paths right from gatsby-config. This makes it pretty easy to maintain.

Workaround # 2

Use splat routes for the pages that need query params.

  • Move all of your gatsby pages needing query params into a folder with the same name as the file, and rename them to [...].js. For example, /src/pages/blog.js becomes /src/pages/blog/[...].js

Using this approach, you will need to change the files in /src/pages. This is a bit more of a hassle than workaround # 1, and harder to maintain. Not sure if this will work for programmatically created pages, either.

justinmahar avatar Nov 05 '21 13:11 justinmahar

Thanks for the workarounds, @justinmahar! I'll see if one of them fixes the issue for me when I have time. It would be really nice if this issue gets fixed before then.

ChrisCrossCrash avatar Nov 07 '21 21:11 ChrisCrossCrash

Has anyone solved this problem yet? If not I would be willing to make a PR

byronlanehill avatar Dec 08 '21 22:12 byronlanehill

@byronlanehill I haven't. This issue was just the straw that broke the camel's back and made me switch the site I was having problems with over to Next JS. I'm only aware of the workaround that @justinmahar posted previously on this issue.

ChrisCrossCrash avatar Dec 09 '21 07:12 ChrisCrossCrash

@byronlanehill If you would be willing to make a PR, that would be fantastic. Unfortunately, because of the particularities of the app I'm working on, we can't use the workaround without hard-coding a bunch of paths, and that's simply unmaintainable. Without this package being updated, we'd have to switch to a different package.

Ksan8 avatar Jan 06 '22 18:01 Ksan8

@Ksan8 I actually recently switched to using use-query-params directly in my layout that wraps each page as it is only a few lines of code and reduces complexity in my opinion. If you'd like me to share the code is be more than happy to

byronlanehill avatar Jan 06 '22 18:01 byronlanehill

@byronlanehill That would be fantastic. Thank you!

Ksan8 avatar Jan 06 '22 18:01 Ksan8

wrapPageElement.jsx

import React from 'react';
import { navigate } from 'gatsby';
import { QueryParamProvider } from 'use-query-params';

function generatePath(location) {
  return location.pathname + location.search;
}

const history = {
  push: (location) => {
    navigate(generatePath(location), { replace: false, state: location.state });
  },
  replace: (location) => {
    navigate(generatePath(location), { replace: true, state: location.state });
  },
};

export const wrapPageElement = ({ element, props }) => (
  <QueryParamProvider history={history} location={props.location}>
    {element}
  </QueryParamProvider>
);

gatsby-browser.js

export { wrapPageElement } from './wrapPageElement';

gatsby-ssr.js

export { wrapPageElement } from './wrapPageElement';

Test before putting into production of course, but this has worked for me adequately. I don't think it would keep location.state if you needed that, and it wouldn't keep hashes by default either.

byronlanehill avatar Jan 06 '22 19:01 byronlanehill

shouldUpdateScroll.js

// If the pathname hasn't changed on an update, such as changing a query parameter
// then the page should not scroll to top.
export function shouldUpdateScroll({ prevRouterProps, routerProps }) {
  return prevRouterProps?.location?.pathname !== routerProps.location.pathname;
}

gatsby-browser.js

export { shouldUpdateScroll } from './shouldUpdateScroll';

I also keep this in the same project. It preserves the scroll if only query parameters changed, which is required in most cases when you are using use-query-params.

byronlanehill avatar Jan 06 '22 19:01 byronlanehill

I will see about publishing a PR this weekend so we can get the functionality into this plugin, but if it is urgent you can add this directly to your project for now.

byronlanehill avatar Jan 06 '22 19:01 byronlanehill

@Ksan8 I updated the code for the wrapPageElement API and that should fix the previous problems I mentioned.

byronlanehill avatar Jan 06 '22 19:01 byronlanehill

@byronlanehill Worked like a charm! I do notice that sometimes there is a quick page-reload flicker, but it doesn't seem to be happening consistently. Thanks again.

Ksan8 avatar Jan 06 '22 21:01 Ksan8

@Ksan8 Is it happening in production or just in dev?

byronlanehill avatar Jan 07 '22 16:01 byronlanehill

@byronlanehill The flicker seems to happen (intermittently) with both a dev and prod build, though I haven't yet deployed to Staging to check fully. It's not a major concern, just something I noticed.

Ksan8 avatar Jan 07 '22 16:01 Ksan8

Okay, let me know how that goes! I will likely use this exact code to make a PR for this package, so hopefully we can get that bug worked out before then

byronlanehill avatar Jan 07 '22 16:01 byronlanehill

Any chances this will get fixed?

stevepepple avatar Mar 04 '22 23:03 stevepepple

Bumping this again, running into this issue myself.

"gatsby-plugin-use-query-params": "^1.0.1",
"use-query-params": "^1.2.3",
"gatsby": "^4.3.0",

Brydom avatar Apr 21 '22 21:04 Brydom

I managed to get around the 404 issue by removing both use-query-params + gatsby-plugin-use-query-params and used other library instead (query-string for grabbing params works well for me with useState). Then I use window.history.replaceState({...}) to update my query/page.

dumle11 avatar May 03 '22 10:05 dumle11

I can confirm that workaround #1 works.

aemaem avatar May 05 '22 08:05 aemaem

Replacement Hook Solution

I've created a hook solution that incorporates the suggestions from @dumle11 above to eliminate the need for this plugin altogether.

This hook solution is now available via the npm library react-use-query-param-string.

You can view the code for the hook code here.

justinmahar avatar May 14 '22 17:05 justinmahar

So i have two components that I'm using useQueryParamString one to set the query param and one to get it. So when i change the query with my setter component it doesn't re-render and the second component never gets the new query param value

ixhukellari avatar May 20 '22 19:05 ixhukellari

Ahh yeah, that's a use case the hook def doesn't cover. I might end up making a context wrapper for this to address that.

justinmahar avatar May 20 '22 20:05 justinmahar

@justinmahar Thank you for spending your time on this

ixhukellari avatar May 20 '22 20:05 ixhukellari

@ixhukellari Alright! So I tackled this for a bit and decided to go with a lighter weight event emitter approach. When changing params, all hooks fetch the latest value.

All you have to do is update the library and things should magically work ;)

npm install react-use-query-param-string@latest

Let me know if that fixes the issue.

justinmahar avatar May 21 '22 00:05 justinmahar

@justinmahar Sorry i just saw this. I just tested it and it works. Thank you so much!

ixhukellari avatar May 23 '22 13:05 ixhukellari

@justinmahar Sorry i just saw this. I just tested it and it works. Thank you so much!

Welcome!

justinmahar avatar May 23 '22 13:05 justinmahar

@justinmahar your cool new hook worked for me too! Thanks for putting that together 💪

jwhubert91 avatar Oct 05 '22 22:10 jwhubert91

@justinmahar -- came back here to thank you for your contribution. it has solved the issue after a bit of head scratching.

lawrenceong001 avatar Oct 13 '22 14:10 lawrenceong001