Search code examples
reactjsnext.jsuse-effectserver-side-rendering

How to rerender a page when the query changes in next.js?


I'm using next.js for online shop project and I have a page that shows best selling lots (bestSellings.tsx). In this page there is a select box that sorts the lots ordering by different factors and each factor has an unique value:

  <select name="ec-select" id="ec-select" onChange={changeSelectBox}>
                                    <option  value="1" selected={false} disabled={false}>state</option>
                                    <option value="2" >name,asc</option>
                                    <option value="3">name,dec</option>
                                    <option value="4">price,asc</option>
                                    <option value="5">price,dec</option>
                                </select> 

I want to pass query for each type of sorts, for example when user click option value='2' It must go to :'bestSellings/sort=2' so I defined changeSelectBox function:

   const changeSelectBox=(e:React.ChangeEvent<HTMLSelectElement>)=>{
             const value=e.target.value
            router.push({pathname:`/bestSelling/`,query:{...query,sort:value}})
            }

Everything was ok until I tried to change lotsInBestSelling array to sorted array by changing the query. This is the whole page:

const BestSelling = ({query}:{query:any}) => {
 const [lotsInBestSellings,setLotsInBestSellings]:any=useState([])

 //to sort array 
   const sortArray=(array:any[],property:any,dec:boolean)=>{
    console.log('s')
    console.log(typeof property)
    const compare=(a:any,b:any)=>{
     
      if(typeof a[property] ===`string`){
        
    console.log('string')
        if (dec==false){
      return a[property].localeCompare(b[property])
   
        }
        else{
          return b[property].localeCompare(a[property])
        }   
      }
      else{
        if (dec==false){
          console.log('number')
          return a[property]-b[property]
        
          
            
        }
        else{
          return b[property]-a[property]
          // if(a[property]<b[property]){
          //   return 1
          // }
          // else if(a[property]>b[property]){
          //   return -1
          // }
        
          //   return 0
          
           
        } 
      }
    
  
      
    }
  array=array.sort(compare)
  return array
    
  }


const initializeLotsInBestSelling=async()=>{
    if(query!=undefined){
        if(query.sort!=undefined){
         const sort=query.sort
         //cases of sorts
         var sortedArray=[...lotsInBestSellings]
         switch (sort){
            
             case `1`:
                   
                    break;
                 
                 case `2`:
                   console.log(`2`)
                  sortedArray=await sortArray(`title`,false)
                   break;
                   case `3`:
                    sortedArray=await sortArray(`title`,true)
                     break;
                     case `4`:
                     sortedArray=await sortArray(`price`,false)
                     break;
                     case `5`:
                      sortedArray=await  sortArray(`price`,true)
                       break;
                
           }
           setLotsInBestSellings([...sortedArray])
        }
    }
}
    
//to get bestSelling from API
  const bestSellings=async()=>{
    
           try{
          const lots =await getBestSellings();
        console.log(lots)
        setLotsInBestSellings(lots)
        

           }
           catch(er){
                console.log(er)
           }
    }
}

  useEffect(()=>{
       console.log("ue")
        bestSellings();
        initializeLotsInBestSelling();
        setTimeout(()=>changeLoading(false),4000)
       
    },[loading,lotsInBestSellings])

return(<>
  {console.log(lotsInBestSellings)}

 <select name="ec-select" id="ec-select" onChange={changeSelectBox}>
                                    <option  value="1" selected={false} disabled={false}>state</option>
                                    <option value="2" >name,asc</option>
                                    <option value="3">name,dec</option>
                                    <option value="4">price,asc</option>
                                    <option value="5">price,dec</option>
                                </select> 
   { lotsInBestSellings.length!=0 && (lotsInBestSellings.map((el:any,i:number)=>{
                                  
                                   return (
                                  
                                   <div key={el.id} className={`col-lg-4 col-md-6 col-sm-6 col-xs-6 mb-6 pro-gl-content ${listStyle==true? "width-100":" "}`}>
                                      <div className="ec-product-inner">
                                   <ReactLoading  key={i} height={"10vh"} width={'10vw'}  color={"#3474d4"}/>
                              </div>
                              </div>
                              )
                                  }))}
</>)

The problem is when I change query and then change lotsInBestSelling, I have the old and unsorted version of lotsInBestSelling. When I logged in useEffect I discovered that useEffect runs only one time and it didn't run when the query changes! I just want page to be refreshed when the user changes type of sort and as It's not a dynamic route I think it's not professional to use getStaticProps or getServerSideProps.


Solution

  • Well, I solved my problem by correcting some mistakes:

    1- acording to julio's comment I forgot passing first parameter to sortArray function(I should pass an array as the first argument), so I changed initializeLotsInBestSelling() function(notice the sortArray function) and this is changed area:

     const initializeLotsInBestSelling=async()=>{
    
                if(query.sort!=undefined){
                 const sort=query.sort
                 //cases of sorts
                 console.log(lots)
                  sortedArray=lots
                 switch (sort){
                    
                     case `1`:
                           
                            break;
                         
                         case `2`:
                           console.log(`2`)
                          sortedArray=await sortArray(lots,`title`,false)
                      
                           break;
                           case `3`:
    
                            sortedArray=await sortArray(lots,`title`,true)
                             break;
                             case `4`:
                             sortedArray=await sortArray(lots,`price`,false)
                             break;
                             case `5`:
                              sortedArray=await  sortArray(lots,`price`,true)
                               break;
                        
                   }
                   console.log([...sortedArray])
                   setLotsInBestSellings([...sortedArray])
                }
    }
    

    2-I used query as a peace of prop ({query}), so query was empty. In static routes like this we should get query using router.query so I changed query:

    query=router.query
    

    3-After user changed the option of the selectBox, the page was routing to page with query. but lotsInBestSellings didn't change anymore. so it was necessary to initializeLotsinBestSelling when changeSelectBox function is triggered. so I changed changeSelectBox() function to :

      const changeSelectBox=(e:React.ChangeEvent<HTMLSelectElement>)=>{
    changeLoading(true)
              
                 const value=e.target.value
                  await initializeLotsInBestSelling();
                router.push({pathname:`/bestSelling/`,query:{...query,sort:value}})
                }
    

    Now I don't need to add [lotsInBestSellings] at the and of the useEffect.