skip to Main Content

I’ve created two custom hooks usePagination and useSearch. These handle the logic for paging and searching and only expose getters/setters for the current page and search query.

Here’s how I would use them in my list component:

const List = () => {
  const [currentPage, setCurrentPage] = usePagination()
  const [searchQuery, setSearchQuery] = useSearch()

  const queryParams = useMemo(() => ({
    page: currentPage,
    search: searchQuery
  }), [currentPage, searchQuery])

  // Pass the `queryParams` to an API endpoint to load my list items.
}

When the search query changes, I need to reset the current page to 1. Something like this:

useEffect(()=>{
  setCurrentPage(1)
}, [searchQuery, setCurrentPage])

Of course, with the above logic, this results in two API calls being made. One when the search query changes and then another immediately after when the current page value is reset.

Is there any way to combine the two values from my custom hooks currentPage and searchQuery into a combined state so that I can update both the page number and the query simultaneously? In other words, is there anything I can do here other than scrap my individual hooks and use a single state for everything?

E.g.

const [queryState, setQueryState] = useState({
  currentPage: 1,
  searchQuery: ""
})

2

Answers


  1. Yes and you almost got it right. You could do something like this:

    const [queryState, setQueryState] = useState({
      currentPage: 1,
      searchQuery: ""
    })
    
    setQueryState((previousState) => {
      // change the state
      return previousState;
    })
    

    You could even wrap it into a hook that takes the initial state and returns the getter and a modified setter. Something like the following:

    // hook body
    const [queryState, setQueryState] = useState(initialState)
    
    const modifiedSetter = (newValues) => {
     setQueryState((previousState) => {
        return {...previousState, ...newValues};
     })
    }
    
    return [queryState, modifiedSetter]
    

    and the usage for it would be:

    const [queryState, updateQuerySate] = useMyCustomHook({...someinitialState})
    
    // Well, getter works the same: queryState.searchQuery
    
    // but the setter goes like this
    
    updateQuerySate({searchQuery: 'whatever'});
    // or
    updateQuerySate({searchQuery: 'whatever', currentPage: 1});
    
    Login or Signup to reply.
    1. Can you explain why you need the setState/setCurrentPage function as a dependency?

    2. As an intended effect, whenever a useState value changes the entire component is re-rendered, and that’s why you have two API calls, because both states are causing a re-rendering.

    Solution: Assuming you only want to make a new API call when searchQuery changes, and not when currentPage data changes, React provides useRef which you can use to store/update data without re-rendering and making costly API calls.

    Example (in your usePagination hook):

    function usePagination() {
       const currentPage = useRef(0);
         
       //make API call...
       
       //if (currentPage.current === 0) {...}
    
       return currentPage
    }
    

    Then in List:

    const List = () => {
      const currentPage = usePagination()
      
      useEffect(()=>{
         currentPage.current = 1
      }, [searchQuery])
    
      //...
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search