Skip to content
Advertisement

How do I properly use useSelector + createSelector (from ‘reselect’) in React app?

I am using reselect lib in my React project. Here is a code:

const App = () => {

  // const { tickets, isFetching, error } = useSelector(({ tickets }) => tickets)   // <- this works good

  const { sorterTabs, selected } = useSelector(({ sorter }) => sorter)
  const { isFetching } = useSelector(({ tickets }) => tickets)
  const dispatch = useDispatch()
  const tickets = useSelector(sorterSelector)   // <- this doesn't work good     

  const handleSorterClick = ({ target }) => {
    dispatch(getSelectedSorter(target.getAttribute('name')))
  }

  const renderedTickets = tickets.map((ticket) => {   // <- problem: I have undefined here because selector returns undefined
    const src = `http://pics.avs.io/99/36/${ticket.carrier}.png`
    return (
      <Ticket
        key={ticket.price + ticket.segments[0].duration}
        ticket={ticket}
        price={ticket.price}
        src={src}
        segment={ticket.segments}
      />
    )
  }).slice(0,5)

  useEffect(() => {
    dispatch(fetchData())
  }, [])

  return (
    <div className="container">
      <Header />
      <div className="content">
        <Filter />
        <div className="content__item">
          <div className="content__item__sorting">
            <Sorter
              sorterTabs={sorterTabs}
              selected={selected}
              handleSorterClick={handleSorterClick}
            />
          </div>
          {isFetching 
            ? 
              <img src={preloader} alt="preloader" />
            : 
              <div className="content__item__tickets">
                {renderedTickets}
              </div>
          }
        </div>
      </div>
    </div>
  )
}

Code for selector:

const allTickets = state => state.tickets.tickets  // <- this gets undefined 
const selected = state => state.sorter.selected

export const sorterSelector = createSelector(
  [allTickets, selected],
  (tickets, selec) => {
    if (selec === "Самый дешевый") {
      tickets.sort((a, b) => {
        return a.price - b.price
      })
    } else {
      tickets.sort((a, b) => {
        return (a.segments[0].duration + a.segments[1].duration) - (b.segments[0].duration + b.segments[1].duration)
      })
    }
  }
)

As I mentioned in the App’s code, first commented line (where I destructure the object) works good: I’m fetching tickets from the server, put them in redux state, get them in App’s component and render them.

But if I create selector by createSelector function from ‘reselect’ lib and put it in useSelector function like I mentioned in the code const tickets = useSelector(sorterSelector), I got mistake: TypeError: Cannot read property 'map' of undefined. Tickets are not returned from the state, but state is empty. That means that fetchData function didn’t work.

What am I doing wrong here? How to use it properly?

UPD: this doesn’t work too const tickets = useSelector((state) => sorterSelector(state))

Advertisement

Answer

All you need is proper return in selector:

export const sorterSelector = createSelector(
  [allTickets, selected],
  (tickets, selec) => {
    if (tickets.isFetching || tickets.error) {
      return []
    }
    if (selec === "Самый дешевый") {
      return [...tickets.tickets].sort((a, b) => a.price - b.price)
    } else {
      return [...tickets.tickets].sort((a, b) => (a.segments[0].duration + a.segments[1].duration) - (b.segments[0].duration + b.segments[1].duration))
    }
  }
)
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement