reactjsreduxreact-reduxreselect

Pass a whole object to redux reselect selector, but change it only if one property of the object changes


Started working with Reselect and there's one thing I can't seem to find an answer for.

Say I have a helper fn (getVehicleList) which does some heavy calculations, so I don't want it to re-run too much. I use state selectors to get the properties I need, something like:

const getVehicles = (state) => state.vehicles.vehicles;
const getVehicle = (state) => state.vehicles.vehicle;
const getUserId = (state) => state.auth.user.id;

I then have implemented the createSelector:

const getVehicles = createSelector(
  [getVehicles,
   getVehicle,
   getUserId],
  (vehicles, vehicle, id) => getVehicleList(
    vehicles,
    vehicle,
    id,
  ),
);

Now, vehicle returns an object with multiple fields. If any of these fields change, the object changes and so everything is recomputed again. Is there a way to stop this recomputing until the id and only the id of the vehicle changes?

I tried doing a state selector for the id, like

const getVehicle = (state) => state.vehicles.vehicle.id;

But that doesn't work for me, cause I need the whole vehicle object inside my helper fn and not just the id.

Thanks in advance for the help!


Solution

  • You can try the following:

    const getVehicles = (state) => state.vehicles.vehicles;
    const getVehicle = (state) => state.vehicles.vehicle;
    const getUserId = (state) => state.auth.user.id;
    const selectVhicleId = createSelector(
      [getVehicle],
      ({ id }) => id //return only the id
    );
    const selectVehicles = createSelector(
      [getVehicles, selectVhicleId, getUserId],
      (vehicles, vehicleId, id) =>
        getVehicleList(vehicles, { id: vehicleId }, id)
    );
    

    Here is some information about how I use reselect with React.

    Here is an example that re calculate vehicles when vehicle.id changes (or any of the other dependencies). It will not re calculate if other values of vehicle change so the vehicle used getVehicleList gets a stale vehicle passed to it that is only refreshed when vehicle.id changes:

    const getVehicles = (state) => state.vehicles.vehicles;
    const getVehicle = (state) => state.vehicles.vehicle;
    const getUserId = (state) => state.auth.user.id;
    const createSelectVehicles = (vehicle) =>
      createSelector([getVehicles, getUserId], (vehicles, id) =>
        getVehicleList(vehicles, vehicle, id)
      );
    
    const Component = () => {
      //only re calculate vehicle if vehicle.id changes
      const vehicle = useSelector(
        getVehicle,
        (a, b) => a?.id === b?.id
      );
      //only create the selector when vehicle changes
      //  vehicle only changes when vehicle.id changes
      const selectVehicles = React.useMemo(
        () => createSelectVehicles(vehicle),
        [vehicle]
      );
      //vehicles is re calculated when vehicle.id changes
      //  or when state.vehicles.vehicles changes or
      //  when state.auth.user.id changes
      const vehicles = useSelector(selectVehicles);
    };