Cómo utilizar Downshift en casos de uso comunes de menús desplegables

Introducción

Índice
  1. Introducción
  • Prerrequisitos
  • Paso 1: Configuración de la aplicación
    1. Descripción general de los conceptos de Downshift
  • Paso 2: Creación de un campo de selección
  • Paso 3: Cómo usar Downshift con Axios
  • Paso 4: creación de un menú desplegable con Downshift
  • Paso 5: Creación de formularios con Downshift
  • Paso 6: Uso de Downshift con Popper.js
  • Conclusión
  • Downshift es una biblioteca que te ayuda a crear componentes React de entrada mejorados, simples, flexibles y compatibles con WAI-ARIA. Su principal uso es la creación de componentes de autocompletado, pero también se puede utilizar para crear componentes desplegables.

    En este tutorial, repasaremos algunos casos de uso comunes resueltos con Downshift.

    Prerrequisitos

    Para seguir este tutorial, necesitas tener Node y NPM instalados en tu máquina. Un conocimiento básico de React te ayudará a aprovechar al máximo este tutorial.

    Si no tiene instalado Node.js, vaya al sitio web de Node e instale la versión recomendada para su sistema operativo.

    Paso 1: Configuración de la aplicación

    Usaremos React Create React Apppara crear una aplicación React simple sin configuración de compilación. Si no la tienes Create React Appinstalada, ejecútala npm i create-react-appen tu terminal para instalarla. Una vez que la tengas en tu máquina, ejecuta el siguiente comando para configurar un nuevo proyecto React en un directorio llamado downshift-examplesy muévete a este nuevo directorio ejecutando estos comandos:

    1. create-react-app downshift-examples
    2. cd downshift-examples

    Una vez que esté en el downshift-examplesdirectorio, ejecute el siguiente comando para instalar Downshift y algunos otros paquetes:

    1. yarn add downshift axios react-popper

    Abra el App.cssarchivo en la srccarpeta y agregue los siguientes estilos:

    fuente/Aplicación.css

        input {      margin: 1rem;      width: 20rem;      padding: 1rem .5rem;    }    .downshift-dropdown {      margin: 0 auto;      width: 20rem;      border: 1px solid whitesmoke;      border-bottom: none;    }    .dropdown-item {      padding: 0.5rem;      cursor: pointer;      border-bottom: 1px solid whitesmoke;      font-size: 1rem;      text-align: left;    }    .dropdown-button {      padding: 0.6rem;      border-radius: 3px;      background: white;      cursor: pointer;    }    .popper-div {      flex: 1;      display: flex;      align-items: center;      justify-content: center;      margin-top: 5rem;    }    .popper-item {      padding: .5rem;      border-bottom: 1px solid whitesmoke;    }

    Con todo configurado, veamos algunos conceptos básicos de Downshift.

    Descripción general de los conceptos de Downshift

    Al usar Downshift, el único componente que necesitamos es Downshift /. Llamamos al Downshift /componente y le pasamos algunas propiedades y hace su magia. Estas son algunas de las propiedades más utilizadas:

    1. onChange:Esta función se llama cuando el usuario selecciona un elemento y el elemento seleccionado ha cambiado. Devuelve el selectedItem.
    2. itemToString:Esta función se utiliza para determinar el valor de la cadena del elemento seleccionado que se utiliza para calcular el inputValue.
    3. inputValue:Esto representa el valor que debe tener el campo de entrada.
    4. getInputProps:Esta función devuelve las propiedades que debes aplicar al inputelemento que renderizas.
    5. getItemProps:Esta función devuelve las propiedades que debes aplicar a cualquier elemento del menú que representes.
    6. isOpen:Este es un valor booleano que indica si el menú está abierto o no.
    7. selectedItem:Esto representa la entrada del elemento seleccionado actualmente.
    8. render:Aquí es donde se renderiza lo que se desee según el estado de Downshift. Esta función se llama con un objeto.

    Puedes consultar la documentación para ver la lista completa de accesorios. Ahora pongamos en práctica este conocimiento.

    Paso 2: Creación de un campo de selección

    Nuestro primer caso de uso de Downshift es un campo de selección. Continúe y cree un DownshiftOne.jsarchivo en la srccarpeta del directorio raíz de su aplicación. Agregue el siguiente código:

    Fuente/DownshiftOne.js

        import React from 'react'    import Downshift from 'downshift';    const books = [      { name: 'Harry Potter' },      { name: 'Net Moves' },      { name: 'Half of a yellow sun' },      { name: 'The Da Vinci Code' },      { name: 'Born a crime' },    ];    const onChange = (selectedBook) = {      alert(`your favourite book is ${selectedBook.name}`)    }    export default () = {      return (        Downshift onChange={onChange} itemToString={books = (books ? books.name : '')}          {/* we'll insert a callback here */}        /DownShift      )    }

    En el código anterior, importamos React y Downshift y declaramos una matriz de libros, una onChangefunción y también un componente funcional que devuelve el Downshift/componente. En el Downshift/componente, pasamos las propiedades onChangey itemToString. Dentro del Downshift/componente, pasaremos otras propiedades a una devolución de llamada y renderizaremos nuestro campo de entrada.

    A continuación, pasaremos las propiedades que necesitamos en una devolución de llamada al Downshift/componente. Actualice su componente funcional con lo siguiente:

    Fuente/DownshiftOne.js

        ...    export default () = {      return (        Downshift onChange={onChange} itemToString={books = (books ? books.name : '')}          // pass the downshift props into a callback          {({ getInputProps, getItemProps, isOpen, inputValue, highlightedIndex, selectedItem, getLabelProps }) = (            div              // add a label tag and pass our label text to the getLabelProps function              label style={{ marginTop: '1rem', display: 'block' }} {...getLabelProps()}Choose your favourite book/label br /              // add our input element and pass our placeholder to the getInputProps function              input {...getInputProps({ placeholder: "Search books" })} /              // if the input element is open, render the div else render nothing              {isOpen ? (                div className="downshift-dropdown"                  {                    // filter the books and return items that match the inputValue                    books                      .filter(item = !inputValue || item.name.toLowerCase().includes(inputValue.toLowerCase()))                      // map the return value and return a div                      .map((item, index) = (                        div                          className="dropdown-item"                          {...getItemProps({ key: item.name, index, item })}                          style={{                            backgroundColor: highlightedIndex === index ? 'lightgray' : 'white',                            fontWeight: selectedItem === item ? 'bold' : 'normal',                          }}                          {item.name}                        /div                      ))                  }                /div              ) : null}            /div          )}        /Downshift      )    }

    En el fragmento de código anterior, pasamos nuestras propiedades Downshift como parámetros a una devolución de llamada:

        {({ getInputProps, getItemProps, isOpen, inputValue, highlightedIndex, selectedItem, getLabelProps }) = ()}

    En la devolución de llamada, agregamos nuestra etiqueta de entrada y le pasamos nuestras getInputPropspropiedades:

        input {...getInputProps({ placeholder: "Search books" })} /

    A continuación, comprobamos si el elemento de entrada está abierto. Si lo está, devolvemos un divelemento que contiene nuestro menú y, nullsi no lo está, devolvemos:

        { isOpen ? (div className="downshift-dropdown".../div) : null }

    Por último, en el divelemento que estamos devolviendo, filtramos nuestra matriz de libros y devolvemos solo los elementos que incluyen el inputValue. Luego, mapeamos los libros filtrados y los representamos en la página.

    También pasamos la getItemPropsfunción a la función divrenderizada map. Devuelve las propiedades que aplicamos al renderizar los elementos.

    Importemos nuestro componente al Appcomponente principal y veamos nuestro campo de selección funcional:

    fuente/App.js

        import React, { Component } from 'react';    import logo from './logo.svg';    import './App.css';    import DownshiftOne from './DownshiftOne'; // import the component    class App extends Component {      render() {        return (          div className="App"            header className="App-header"              img src={logo} className="App-logo" alt="logo" /              h1 className="App-title"Welcome to React/h1            /header            DownshiftOne / // render the component          /div        );      }    }    export default App;

    Asegúrate de que tu servidor esté funcionando ejecutándolo npm starten tu terminal. Si lo abres http://localhost:3000en tu navegador, verás que la aplicación se está ejecutando.

    Paso 3: Cómo usar Downshift con Axios

    En nuestro próximo ejemplo, utilizaremos Downshift para crear un campo de búsqueda de películas. En la srccarpeta, crea un DownshiftTwo.jsarchivo y agrega el siguiente código:

    Fuente/DownshiftTwo.js

        import React, { Component } from 'react'    import Downshift from 'downshift';    import axios from 'axios';    export default class DownshiftTwo extends Component {      constructor(props) {        super(props)        this.state = {          movies: []        }        this.fetchMovies = this.fetchMovies.bind(this)        this.inputOnChange = this.inputOnChange.bind(this)      }      // onChange method for the input field      inputOnChange(event) {        if (!event.target.value) {          return        }        this.fetchMovies(event.target.value)      }      // input field for the Downshift / component      downshiftOnChange(selectedMovie) {        alert(`your favourite movie is ${selectedMovie.title}`)      }      // method to fetch the movies from the movies API      fetchMovies(movie) {        const moviesURL = `https://api.themoviedb.org/3/search/movie?api_key=APIKeyquery=${movie}`;        axios.get(moviesURL).then(response = {          this.setState({ movies: response.data.results })        })      }      render() {        return (          Downshift onChange={this.downshiftOnChange} itemToString={item = (item ? item.title : '')}          // pass the downshift props into a callback            {({ selectedItem, getInputProps, getItemProps, highlightedIndex, isOpen, inputValue, getLabelProps }) = (              div                // add a label tag and pass our label text to the getLabelProps function                label style={{ marginTop: '1rem', display: 'block' }} {...getLabelProps()}Choose your favourite movie/label br /                // add a input tag and pass our placeholder text to the getInputProps function. We also have an onChange eventlistener on the input field                input {...getInputProps({                  placeholder: "Search movies",                  onChange: this.inputOnChange                })} /                // if the input element is open, render the div else render nothing                {isOpen ? (                  div className="downshift-dropdown"                    {                      // filter the movies in the state                      this.state.movies                        .filter(item = !inputValue || item.title.toLowerCase().includes(inputValue.toLowerCase()))                        .slice(0, 10) // return just the first ten. Helps improve performance                        // map the filtered movies and display their title                        .map((item, index) = (                          div                            className="dropdown-item"                            {...getItemProps({ key: index, index, item })}                            style={{                              backgroundColor: highlightedIndex === index ? 'lightgray' : 'white',                              fontWeight: selectedItem === item ? 'bold' : 'normal',                            }}                            {item.title}                          /div                        ))                    }                  /div                ) : null}              /div            )}          /Downshift        )      }    }

    En el código anterior, tenemos un componente de clase en el que lo renderizamos Downshift/y le pasamos nuestras propiedades. En el campo de entrada del Downshift/componente, tenemos un onChangedetector de eventos que detecta nuevas entradas en el campo.

    Si hay una entrada, llamamos al fetchMoviesmétodo que toma el valor del campo de entrada y realiza una solicitud AJAX a la API de películas mediante Axios. Establecemos la respuesta de la solicitud en el estado del componente y luego los filtramos para devolver el elemento coincidente como se hizo en el ejemplo anterior.

    Importa y renderiza el componente en el Appcomponente principal como hicimos en el ejemplo anterior. Visita tu navegador e intenta buscar tu película favorita.

    Paso 4: creación de un menú desplegable con Downshift

    Otro caso de uso de Downshift es el de potenciar los menús desplegables. La API de Dropdown nos ayuda a crear componentes de menú desplegable simples. Creemos un DownshiftThree.jsarchivo en la srccarpeta y veamos cómo lograrlo.

    En el DownshiftThree.jsarchivo, agregue el siguiente código:

    Fuente/DownshiftThree.js

        import React, { Component } from 'react'    import Downshift from 'downshift';    export default class DownshiftThree extends Component {      constructor(props) {        super(props)        this.books = [          { name: 'Harry Potter' },          { name: 'Net Moves' },          { name: 'Half of a yellow sun' },          { name: 'The Da Vinci Code' },          { name: 'Born a crime' },        ];        this.state = {          // currently selected dropdown item          selectedBook: ''        }        this.onChange = this.onChange.bind(this)      }      onChange(selectedBook) {        this.setState({ selectedBook: selectedBook.name })      }      render() {        return (          Downshift onChange={this.onChange} selectedItem={this.state.selectedBook} itemToString={books = (books ? books.name : '')}          // pass the downshift props into a callback            {({ isOpen, getToggleButtonProps, getItemProps, highlightedIndex, selectedItem: dsSelectedItem, getLabelProps }) = (              div                // add a label tag and pass our label text to the getLabelProps function                label style={{ marginTop: '1rem', display: 'block' }} {...getLabelProps()}Select your favourite book/label br /                // add a button for our dropdown and pass the selected book as its content if there's a selected item                button className="dropdown-button" {...getToggleButtonProps()}                  {this.state.selectedBook !== '' ? this.state.selectedBook : 'Select a book ...'}                /button                div style={{ position: 'relative' }}                  // if the input element is open, render the div else render nothing                  {isOpen ? (                    div className="downshift-dropdown"                      {                        // map through all the books and render them                        this.books.map((item, index) = (                          div                            className="dropdown-item"                            {...getItemProps({ key: index, index, item })}                            style={{                              backgroundColor: highlightedIndex === index ? 'lightgray' : 'white',                              fontWeight: dsSelectedItem === item ? 'bold' : 'normal',                            }}                            {item.name}                          /div                        ))                      }                    /div                  ) : null}                /div              /div            )}          /Downshift        )      }    }

    En el código anterior, tenemos un DownshiftThreecomponente de clase donde representamos el Downshift/componente. En la devolución de llamada que se le pasa, tenemos un botón donde pasamos la getToggleButtonPropsfunción. El botón contiene un operador ternario donde establecemos el contenido del botón en función de si el selectedBookestado del componente está establecido.

    A continuación, llamamos a la isOpenpropiedad para ver si el menú desplegable está abierto o no. Si está abierto, mapeamos todos los libros y los representamos en el menú desplegable.

    En el onChangemétodo que se pasa al Downshift/componente, cada vez que se selecciona un elemento, le asignamos el estado, lo que actualiza el contenido del botón. Importa y renderiza el componente en el Appcomponente principal y vuelve a cargar el navegador para ver la aplicación en este punto.

    Paso 5: Creación de formularios con Downshift

    En este ejemplo, utilizaremos un componente de entrada Downshift como campos de entrada en un formulario e intentaremos enviar los datos del formulario. En el srcdirectorio, crearemos dos archivos: DownshiftInputField.jsy DownshiftFour.js.

    En el DownshiftInputField.js, crearemos un componente de entrada con Downshift y lo usaremos para representar algunos campos de entrada en el DownshiftFour.jsarchivo. Vamos a crear un componente funcional en nuestro DownshiftInputField.jsarchivo:

    fuente/DownshiftInputField.js

        import React from 'react'    import Downshift from 'downshift';    export default ({ items, onChange, label, placeholder, name }) = {      return (        Downshift onChange={onChange} itemToString={items = (items ? items.name : '')}          {({ getInputProps, getItemProps, isOpen, inputValue, highlightedIndex, selectedItem, getLabelProps }) = (            div            // add a label tag and pass our label text to the getLabelProps function              label style={{ marginTop: '1rem', display: 'block' }} {...getLabelProps()}{label}/label br /              // add an input tag and pass our placeholder text to the getInputProps function              input name={name} {...getInputProps({ placeholder })} /              // if the input element is open, render the div else render nothing              {isOpen ? (                div className="downshift-dropdown"                  {                    items                      // filter the items and return those that includes the inputValue                      .filter(item = !inputValue || item.name.toLowerCase().includes(inputValue.toLowerCase()))                      // map through the returned items and render them to the page                      .map((item, index) = (                        div                          className="dropdown-item"                          {...getItemProps({ key: item.name, index, item })}                          style={{                            backgroundColor: highlightedIndex === index ? 'lightgray' : 'white',                            fontWeight: selectedItem === item ? 'bold' : 'normal',                          }}                          {item.name}                        /div                      ))                  }                /div              ) : null}            /div          )}        /Downshift      )    }

    En el código anterior, nuestro componente funcional incluye una matriz de elementos, una función onChange, una etiqueta y un texto de marcador de posición y, por último, un nombre. El componente devuelve un Downshift/componente que recibe todas las propiedades requeridas en una función de devolución de llamada. En la devolución de llamada, tenemos una etiqueta y un campo de entrada.

    Al igual que en otros ejemplos, pasamos la isOpenpropiedad a un operador ternario. Si el campo de entrada está abierto, filtramos la matriz de elementos para devolver los elementos que coinciden con inputValue, luego asignamos los elementos devueltos y los representamos en el DOM.

    Ahora que nuestro componente de campo de entrada está listo, importémoslo al DownshiftFour.jsarchivo:

    Fuente/DownshiftFour.js

        import React, { Component } from 'react'    import DownshiftInputField from './DownshiftInputField';    export default class DownshiftFour extends Component {      constructor(props) {        super(props)        this.state = {          books: [            { name: 'Harry Potter' },            { name: 'Net Moves' },            { name: 'Half of a yellow sun' },            { name: 'The Da Vinci Code' },            { name: 'Born a crime' },          ],          movies: [            { name: 'Harry Potter' },            { name: '12 Strong' },            { name: 'Half of a yellow sun' },            { name: 'Gringo' },            { name: 'Black Panther' },          ],          book: '',          movie: ''        }        this.onSubmit = this.onSubmit.bind(this);        this.onChange = this.onChange.bind(this);      }      onSubmit(event) {        event.preventDefault();        alert(`        Favourite book: ${this.state.book}        Favourite movie: ${this.state.movie}        has been submitted        `)      }      onChange(selectedBook, stateAndHelpers) {        const element = document.querySelector(`#${stateAndHelpers.id}-input`)        this.setState({ [element.name]: selectedBook.name })      }      render() {        return (          form onSubmit={this.onSubmit}            DownshiftInputField              items={this.state.books}              onChange={this.onChange}              label="Select your favourite book"              name="book"              placeholder="Search your favourite book" /            DownshiftInputField              items={this.state.movies}              onChange={this.onChange}              label="Select your favourite movie"              name="movie"              placeholder="Search your favourite movie" /            input type="submit" value="Submit" className="dropdown-button" /          /form        )      }    }

    En nuestro DownshiftFour.jsarchivo, importamos nuestro componente de campo de entrada y creamos un componente de clase. En nuestro estado de componente, tenemos una matriz de libros y películas y representamos nuestro componente de campo de entrada dos veces en un formulario: uno como campo de entrada para libros y otro para películas.

    La función de Downshift onChangetoma un segundo parámetro llamado stateAndHelpersque nos brinda información sobre el estado actual de Downshift.

    En el onChangemétodo, encontramos el campo de entrada con el que el usuario está interactuando actualmente obteniéndolo iddel stateAndHelpersargumento y consultándolo en el DOM. Una vez que tenemos el elemento, lo configuramos en el estado del componente.

    Cuando el usuario pulsa el botón Enviar, el onSubmitmétodo obtiene el elemento seleccionado bookdel movieestado y hace lo que queramos con él.

    Importa y renderiza el DownshiftFour/componente en el Appcomponente principal y probémoslo en el navegador.

    Paso 6: Uso de Downshift con Popper.js

    En nuestro último ejemplo de este artículo, usaremos Popper.js para cambiar las direcciones de una ventana emergente de Downshift. Popper es una biblioteca que hace que la colocación de información sobre herramientas o ventanas emergentes sea una tarea más manejable.

    Ya instalamos el react-popperpaquete al configurar la aplicación, así que creemos un DownshiftFive.jsarchivo en la srccarpeta.

    Agreguemos el siguiente código al nuevo archivo:

    Fuente/DownshiftFive.js

        import React, { Component } from 'react'    import Downshift from 'downshift';    import { Popper, Manager, Target } from 'react-popper';    export default class DownshiftFive extends Component {      // define some default props      static defaultProps = {        positions: [          'top',          'top-start',          'top-end',          'bottom-start',          'bottom',          'bottom-end',        ]      }      render() {        return (          div className="popper-div"          // wrap the whole component in Popper's Manager/ component            Manager              Downshift render={({ inputValue, getInputProps, getItemProps, isOpen, selectedItem, highlightedIndex }) = (                div                // wrap our input element in Popper's Target/ component                  Target                    input {...getInputProps({ placeholder: 'Enter a position' })} /                  /Target                  div className="downshift-dropdown"                    {isOpen ? (                    // pass the selected item to Popper                      Popper                        placement={selectedItem || 'bottom'}                        style={{ backgroundColor: 'skyblue' }}                                              {                        // filter through all the positions and return the ones that include the inputValue                          this.props.positions                            .filter(item = !inputValue || item.includes(inputValue.toLowerCase()))                            // map through all the returned positions and render them                            .map((item, index) = (                              div className="downshift-item popper-item"                                {...getItemProps({ item })}                                key={item}                                style={{                                  cursor: 'pointer',                                  backgroundColor: highlightedIndex === index ? '#bed5df' : 'transparent',                                  fontWeight: selectedItem === item ? 'bold' : 'normal',                                }}                                {item}                              /div                            ))                        }                      /Popper                    ) : null}                  /div                /div              )}/Downshift            /Manager          /div        )      }    }

    En el fragmento de código anterior, creamos una DownshiftFiveclase de componentes con propiedades predeterminadas position. Estas son las posiciones que Popper usará para representar nuestra ventana emergente. En el método de representación del componente, devolvemos un Downshift/componente envuelto en un Manager/componente:

        return (      div className="popper-div"        Manager          Downshift render={({ inputValue, getInputProps, getItemProps, isOpen, selectedItem, highlightedIndex }) = (            {/* all of our remaining code goes here */}          )}          /DownShift        /Manager      /div    )

    El Managercomponente es un contenedor expuesto por Popper que necesita rodear a todos los demás react-poppercomponentes de la página para que se comuniquen entre sí.

    Si observas con atención, verás que estamos pasando una renderpropiedad al Downshift/componente. Esta es otra forma de pasar nuestras propiedades al Downshift/componente. Básicamente, movimos nuestra devolución de llamada y la pasamos a la renderpropiedad.

    En la devolución de llamada que se pasa a la renderpropiedad, envolvemos nuestro campo de entrada en un Target/componente. Esto le informa a Popper que este es el campo de entrada alrededor del cual se debe representar la ventana emergente.

    A continuación, verificamos si nuestra entrada está abierta y renderizamos un Popper/componente y pasamos nuestro selectedItema su placementpropiedad. Esto ayuda a Popper a reposicionar la ventana emergente cada vez que se selecciona una nueva posición. Por último, como en otros ejemplos, filtramos todas las posiciones de propiedad predeterminadas, devolvemos las posiciones que incluyen el inputValue, las mapeamos y las renderizamos en la página.

    Por último, importe y renderice el DownshiftFive/componente en el Appcomponente principal y compruébelo en el navegador. Verá el producto final.

    Conclusión

    En esta publicación, ha creado una aplicación para explorar casos de uso comunes del menú desplegable de React resueltos con Downshift.

    SUSCRÍBETE A NUESTRO BOLETÍN 
    No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

    Subir

    Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion