DEV Community

Cover image for Facilitando la búsqueda por precio dentro de nuestra app de reservas 🏨
caroguerrerosilvera for Ayenda

Posted on • Updated on

Facilitando la búsqueda por precio dentro de nuestra app de reservas 🏨

Para el usuario objetivo de Ayenda, el precio de una habitación juega un papel importante en la toma de decisiones dentro de su recorrido de compra, por tal motivo decidimos añadir un selector de rango de precios dentro de nuestro listado de hoteles, con el fin de mejorar la experiencia en la búsqueda y filtrado, y así proveer mejores resultados.

En este post vamos a compartir cómo fue el proceso de creación de esta característica que facilita al usuario filtrar por precios, permitiendo elegir entre un valor mínimo y máximo.

DE LOS INSIGHTS AL DISEÑO 🎨

Ayenda añade constantemente nuevos hoteles dentro de la cadena. A día de hoy contamos con más de 500 hoteles a disposición de los usuarios en 3 diferentes países y dentro de su propuesta de valor, está ofrecer la mejor experiencia a la hora de reservar a través de nuestra app. A medida que crece el número de hoteles, es importante mejorar la experiencia de búsqueda y compra de los usuarios para ofrecerles resultados relevantes.

Actualmente la app de reservas brinda opciones de ordenamiento y filtrado sobre el listado de hoteles, el selector de rango de precios haría parte del grupo de filtrado. Después de explorar varios diseños y validarlos con usuarios, se definió qué la mejor experiencia era usar un slider múltiple, es decir, con dos marcadores que permitan al usuario establecer un precio mínimo y máximo para filtrar los hoteles. Adicionalmente, se añadió un texto que permite visualizar el rango seleccionado por el usuario. El diseño final se ve como se muestra a continuación:

Image description

DEL DISEÑO A LA SOLUCIÓN 👩‍💻👨‍💻

Para materializar estas ideas en un selector de rango de precios en React Native teníamos dos opciones; hacer un componente con múltiples sliders por nuestra cuenta o usar un componente de un tercero que se ajustara a estos requerimientos.

Después de revisar varios componentes, encontramos uno que cumple con la condición de soportar los dos marcadores que indican el valor mínimo y máximo del rango dentro de un slider, además de tener un buen performance tanto para Android como para iOS y ese fue @ptomasroos/react-native-multi-slider.

Para su instalación ejecutamosnpm install --save @ptomasroos/react-native-multi-slider o yarn install @ptomasroos/react-native-multi-slider dependiendo de tus preferencias.

Una vez instalada esta dependencia, creamos un nuevo archivo llamado PriceSelector.js que contendrá la lógica y los elementos visuales del componente.

import MultiSlider from '@ptomasroos/react-native-multi-slider';

function PriceSelector({ max, min, onChange, selectedRangePrice }) {
  // Nuestro código irá aquí
}

export default PriceSelector;
Enter fullscreen mode Exit fullscreen mode

Este componente necesitará las siguientes props:

max: Valor máximo que el usuario podrá seleccionar.
min: Valor mínimo que el usuario podrá seleccionar.
onChange: Función callback que funciona como listener cuando el usuario ha terminado de mover el slider.
selectedRangePrice: Dado el contexto de nuestra app de reservas, el rango de precios puede estar preestablecido antes de renderizar el componente por primera vez o puede ser modificado de forma externa, por lo que se debe reflejar este cambio tomando como referencia los valores de esta prop.

Ahora veamos cuales props necesita el componente react-native-multi-slider qué acabamos de instalar.

<MultiSlider
  values={[minValue, maxValue]}
  min={min}
  max={max}
  onValuesChange={}
  onValuesChangeFinish={}
/>
Enter fullscreen mode Exit fullscreen mode

values: Valores fijados para establecer el rango.
min: Valor mínimo que puede ser seleccionado.
max: Valor máximo que puede ser seleccionado.
onValuesChange: Callback ejecutado mientras el valor cambia.
onValuesChangeFinish: Callback ejecutado cuando el valor deja de cambiar.

Iremos agregando lo necesario en PriceSelector para que pueda funcionar en conjunto con el componente react-native-multi-slider. Lo primero que añadiremos será un estado para controlar el cambio de valores dentro del componente.

function PriceSelector({ max, min, onChange, selectedRangePrice }) {
  const [priceRange, setPriceRange] = useState({
    minValue: min,
    maxValue: max
  });

  // ...
}
Enter fullscreen mode Exit fullscreen mode

La primera vez que PriceSelector sea renderizado tendrá definido el rango establecido por las propiedades min y max. Es decir, si el min es igual a $20.000 COP y el max es igual a $50.000 COP, deberíamos visualizar el rango de la siguiente manera:

Image description

A continuación añadiremos la función que nos permitirá cambiar el estado que acabamos de agregar:

function handleChangePrice(values) {
  setPriceRange({
    minValue: values[0],
    maxValue: values[1]
  });
}
Enter fullscreen mode Exit fullscreen mode

Esta será ejecutada cada vez que los valores en el estado sean modificados.

Image description

Ahora agregaremos la función que se ejecutará cuando el usuario deje de mover el marcador sobre el slider y ejecutará el onChange que recibimos desde las props.

function handleValuesChangeFinish() {
  onChange(priceRange);
}
Enter fullscreen mode Exit fullscreen mode

Esto lo hacemos para que el componente padre pueda saber cuando el usuario ha cambiado el rango de precios en nuestro selector.

Al unir todo, el componente debería verse así:

function PriceFilter({ max, min, onChange, selectedRangePrice }) {
  const [priceRange, setPriceRange] = useState({
    minValue: min,
    maxValue: max
  });

  function handleChangePrice(values) {
    setPriceRange({
      minValue: values[0],
      maxValue: values[1]
    });
  }

  function handleValuesChangeFinish() {
    onChange(priceRange);
  }

  return <MultiSlider
           values={[minValue, maxValue]}
           min={min}
           max={max}
           onValuesChange={handleChangePrice}
           onValuesChangeFinish={handleValuesChangeFinish}
         />;
}
Enter fullscreen mode Exit fullscreen mode

Por último agregaremos un useEffect que se encargará de detectar y actualizar el componente PriceSelector cuando las props selectedRangePrice, min o max hayan cambiado.

useEffect(() => {
  setPriceRange({
    minValue: selectedRangePrice?.minValue || min,
    maxValue: selectedRangePrice?.maxValue || max
  });

  if (!selectedRangePrice) {
    onChange({ minValue: min, maxValue: max });
  }
}, [selectedRangePrice, onChange, min, max]);
Enter fullscreen mode Exit fullscreen mode

¿Por qué queremos detectar si estas props han cambiado? En el caso de las props min y max, dentro del contexto de nuestra app de reservas, estos valores pueden cambiar cuando el usuario cambia su rango de búsqueda en cuanto a ubicación, como por ejemplo cuando busca hoteles en otra ciudad.

Image description

La prop selectedRangePrice puede cambiar por ejemplo cuando el usuario limpia los filtros o cambia la ubicación en la qué está buscando, y en ese caso es necesario reflejar visualmente los cambios pertinentes, así como para el componente padre.

Añadiremos una validación para cuando las props min y max aún no están definidas, de manera que el componente react-native-multi-slider trabaje correctamente, ya que necesita establecer estos valores por defecto. Además agregaremos el rango seleccionado y un texto para que nuestro componente quede más amigable.

return (
  <>
    <Subtitle size={1}>
      <Subtitle size={1} style={styles.greenText}>
        {' '}
        Rango{' '}
      </Subtitle>{' '}
      de precios:
    </Subtitle>
    <Subtitle size={2} style={styles.prices}>
      <Currency value={priceRange.minValue} /> -{' '}
      <Currency value={priceRange.maxValue} />
    </Subtitle>
    {min && max && (
      <MultiSlider
        values={[minValue, maxValue]}
        min={min}
        max={max}
        onValuesChange={}
        onValuesChangeFinish={}
      />
    )}
  </>
Enter fullscreen mode Exit fullscreen mode

MEJORANDO NUESTRO COMPONENTE 🤩

La versión actual de la app ha liberado este nuevo filtro que acabamos de construir en estas líneas. Ahora debemos revisar nuestras métricas y observar que se logre el impacto esperado. Probablemente pienses que tengamos varias oportunidades de mejora de cara a la experiencia de usuario con este filtro, déjanos tus impresiones en los comentarios.

Top comments (0)