pbeshai / use-query-params

React Hook for managing state in URL query parameters with easy serialization.
https://pbeshai.github.io/use-query-params
ISC License
2.16k stars 96 forks source link

Calling setQuery changes references to query params which are either objects or arrays #230

Closed RomanTokar closed 2 years ago

RomanTokar commented 2 years ago

Actual behavior

import { useEffect } from "react";
import { useQueryParams, withDefault, JsonParam } from "use-query-params";

export default function App() {
  const [query, setQuery] = useQueryParams({
    json: withDefault(JsonParam, {})
  });
  console.log(JSON.stringify(query));

  useEffect(() => {
    setQuery((prev) => ({ ...prev}));
  }, []);

  useEffect(() => {
    console.log("here");
  }, [query.json]);

  return null;
}

Console

image

https://codesandbox.io/s/use-query-params-jsonparam-issue-737tn4?file=/src/App.js

As you can see, whenever we call setQuery and change our params, a reference to query.json is changed and the second useEffect is called.

Expected behavior

I assume that it should work the same as behavior of the useState hook

import { useEffect, useState } from "react";

export default function App() {
  const [query, setQuery] = useState({
    json: {}
  });
  console.log(JSON.stringify(query));

  useEffect(() => {
    setQuery((prev) => ({ ...prev}));
  }, []);

  useEffect(() => {
    console.log("here");
  }, [query.json]);

  return null;
}

Console

image

And I would like to add that every render a reference to query object is being changed as well.

pbeshai commented 2 years ago

The issue is you're creating a new param each render by calling withDefault so useQueryParams doesn't know it can keep the old value. If you define your parameter type once, it will work as expected:



const MyParam = withDefault(JsonParam, {})
export default function App() {
  const [query, setQuery] = useQueryParams({
    json: MyParam
  });
  ...
}
``
RomanTokar commented 2 years ago

@pbeshai Thanks for help and thanks for having such a great package. I guess it would be great if that would be described in the documentation.