45

I know we can replace query params in component based classes doing something along the lines of:

  componentDidMount() {       
    const { location, replace } = this.props;   

    const queryParams = new URLSearchParams(location.search);   
    if (queryParams.has('error')) { 
      this.setError(    
        'There was a problem.'  
      );    
      queryParams.delete('error');  
      replace({ 
        search: queryParams.toString(), 
      });   
    }   
  }

Is there a way to do it with react hooks in a functional component?

Seth McClaine
  • 9,142
  • 6
  • 38
  • 64

2 Answers2

70

For React Router V6 and above, see the answer below.


Original Answer:

Yes, you can use useHistory & useLocation hooks from react-router:


import React, { useState, useEffect } from 'react'
import { useHistory, useLocation } from 'react-router-dom'

export default function Foo() {
  const [error, setError] = useState('')

  const location = useLocation()
  const history = useHistory()

  useEffect(() => {
    const queryParams = new URLSearchParams(location.search)
    
    if (queryParams.has('error')) {
      setError('There was a problem.')
      queryParams.delete('error')
      history.replace({
        search: queryParams.toString(),
      })
    }
  }, [])

  return (
    <>Component</>
  )
}

As useHistory() returns history object which has replace function which can be used to replace the current entry on the history stack.

And useLocation() returns location object which has search property containing the URL query string e.g. ?error=occurred&foo=bar" which can be converted into object using URLSearchParams API (which is not supported in IE).

Ajeet Shah
  • 18,551
  • 8
  • 57
  • 87
  • This won't work with Class Component, how to we handle it there. – rizways Sep 07 '21 at 10:43
  • In a class component, replace `useEffect` with `componentDidMount`, `useState` with `this.state`, `useHistory` with `this.props.history` + `withRouter` HOC. That should work with class component. – Ajeet Shah Sep 07 '21 at 11:47
  • In case anyone is interested, to be used with function components I've created a library `useClearParams` https://github.com/oyalhi/use-clear-params#readme – oyalhi Sep 21 '21 at 00:11
  • 3
    useNavigate in react-router-dom v6 – alerya Jan 19 '22 at 10:05
  • I've used this approach to update a search param based on user selection on the page, but this causes page reload, which is completely unnecessary in my case ... – sr9yar Apr 14 '22 at 11:25
27

Use useSearchParams hook.

import {useSearchParams} from 'react-router-dom';

export const App =() => {
  const [searchParams, setSearchParams] = useSearchParams();

  const removeErrorParam = () => {
    if (searchParams.has('error')) {
      searchParams.delete('error');
      setSearchParams(searchParams);
    }
  }

  return <button onClick={removeErrorParam}>Remove error param</button>
}
Vaelyr
  • 2,841
  • 2
  • 21
  • 34
  • Note that this doesn't remove it from the url (which might be a little confusing depending on the use case) - useHistory is no more in v6 - so some variation of useNavigation would probably be needed... know how to navigate without triggering a re-render? If I recall last time I needed prevention of navigation I messed with navigator from UNSAFE_NavigationContext – Julix Nov 08 '22 at 00:16
  • 1
    https://stackoverflow.com/a/70700037/2848941 hmm .... seems extra navigation _shouldn't_ be necessary, as long as it's in a useEffect? – Julix Nov 08 '22 at 00:18
  • 1
    Added this to my useEffect hook and it works like a charm. – KeatsKelleher Mar 20 '23 at 01:39
  • is this still in react router v6? – Jordan Mackie May 31 '23 at 18:40