Home > Blockchain >  location.search does not update correctly - React - ReactRouterDomV5
location.search does not update correctly - React - ReactRouterDomV5

Time:07-14

In My App Functional Component I Define a Function That Should be Call Once The User Clicked On The Search Button In My MainHeader Functional Component.


function App(props) {
    const location = useLocation();
    const [movies, setMovies] = useState(DUMMY_MOVIES);
    
    const filterMovies = () => {
        console.log(location.search);
    }
    
    return (
        <MainHeader onFilterMovies={filterMovies}>
            <Switch>
                <Route path="/" exact>
                    <Redirect to="/movies" />
                </Route>
                <Route path="/movies" exact>
                    <Movies movies={movies} />
                </Route>
                <Route path="/movies/:movieId">
                    <MovieDetails />
                </Route>
            </Switch>
        </MainHeader>
    );
}

In The MainHeader Component There Is A Submit Handler That Will Execute Once The User Click the Search Button, And In There(SubmitHandler) I Append searchTerm As A Key And searchTermValue As The Value Of The Query Parameter And Replace The Page With location.replace(`/movies?searchTerm=${searchTermValue}`). And Finally I Call The OnFitlerMovies Whitch I Receive From props To Inform The App Component That We're DONE!

In App Functional Component Where I Define The FiltrerMovies Function, I Simply Log The Query Parameter Value To The Console.

But The Value Witch I Receive Is The Previous Value Of The Query Parameter. I Mean When I Enter something In The Search Input And Then Click The Search Button I Receive It In FilterMovies Method As An Empty String, But The Next Time, Even If I Dont Enter Anything(I Mean A Empty String), The Correct Value For The Previous Time Get Logged!

const MainHeader = (props) => {
    const history = useHistory();
    const searchTermInputRef = useRef();
    
    const submitHandler = (e) => {
        e.preventDefault();
        
        const searchTermValue = searchTermInputRef.current.value.trim();
        
        history.replace(`/movies?searchTerm=${searchTermValue}`);
        
        props.onFilterMovies();
    };
...

My Guess Is That We Can't Use ReactRouterDomV5 Hooks Inside of A Component Witch Is Not In Route Component.

Now How Can I Solve This Problem?

CodePudding user response:

Issue

The filterMovies callback has a stale closure over the location.search value, so you see the stale value.

Solution

Use a useEffect hook to log the updated location.search value.

Example:

useEffect(() => {
  console.log(location.search);
}, [location.search]);

If you are wanting to use the querySearch param to filter the movies state prior to passing it to the Movies component do this inline.

Example:

function App() {
  const location = useLocation();
  const [movies, setMovies] = useState(DUMMY_MOVIES);

  const searchParams = new URLSearchParams(location.search);
  const searchTerm = searchPararms.get("searchTerm");

  useEffect(() => {
    console.log(location.search);
  }, [location.search]);

  return (
    <MainHeader>
      <Switch>
        <Route path="/" exact>
          <Redirect to="/movies" />
        </Route>
        <Route path="/movies" exact>
          <Movies
            movies={movies.filter(movie => {
              if (searchTerm) {
                return /* searchTerm condition to filter by */
              }
              return true;
            })}
          />
        </Route>
        <Route path="/movies/:movieId">
          <MovieDetails />
        </Route>
      </Switch>
    </MainHeader>
  );
}

If you want to improve performance then use the useMemo hook to memoize the filtered movies value.

Example:

const location = useLocation();
const [movies, setMovies] = useState(DUMMY_MOVIES);

const searchParams = new URLSearchParams(location.search);
const searchTerm = searchPararms.get("searchTerm");

const filteredMovies = useMemo(() => {
  return movies.filter(movie => {
    if (searchTerm) {
      return /* searchTerm condition to filter by */
    }
    return true;
  }
}, [movies, searchTerm]);

...

<Route path="/movies" exact>
  <Movies movies={filteredMovies} />
</Route>
  • Related