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

Introducció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 App
para crear una aplicación React simple sin configuración de compilación. Si no la tienes Create React App
instalada, ejecútala npm i create-react-app
en 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-examples
y muévete a este nuevo directorio ejecutando estos comandos:
- create-react-app downshift-examples
- cd downshift-examples
Una vez que esté en el downshift-examples
directorio, ejecute el siguiente comando para instalar Downshift y algunos otros paquetes:
- yarn add downshift axios react-popper
Abra el App.css
archivo en la src
carpeta 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:
onChange
:Esta función se llama cuando el usuario selecciona un elemento y el elemento seleccionado ha cambiado. Devuelve elselectedItem
.itemToString
:Esta función se utiliza para determinar el valor de la cadena del elemento seleccionado que se utiliza para calcular elinputValue
.inputValue
:Esto representa el valor que debe tener el campo de entrada.getInputProps
:Esta función devuelve las propiedades que debes aplicar alinput
elemento que renderizas.getItemProps
:Esta función devuelve las propiedades que debes aplicar a cualquier elemento del menú que representes.isOpen
:Este es un valor booleano que indica si el menú está abierto o no.selectedItem
:Esto representa la entrada del elemento seleccionado actualmente.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.js
archivo en la src
carpeta 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 onChange
función y también un componente funcional que devuelve el Downshift/
componente. En el Downshift/
componente, pasamos las propiedades onChange
y 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 getInputProps
propiedades:
input {...getInputProps({ placeholder: "Search books" })} /
A continuación, comprobamos si el elemento de entrada está abierto. Si lo está, devolvemos un div
elemento que contiene nuestro menú y, null
si no lo está, devolvemos:
{ isOpen ? (div className="downshift-dropdown".../div) : null }
Por último, en el div
elemento 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 getItemProps
función a la función div
renderizada map
. Devuelve las propiedades que aplicamos al renderizar los elementos.
Importemos nuestro componente al App
componente 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 start
en tu terminal. Si lo abres http://localhost:3000
en 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 src
carpeta, crea un DownshiftTwo.js
archivo 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 onChange
detector de eventos que detecta nuevas entradas en el campo.
Si hay una entrada, llamamos al fetchMovies
mé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 App
componente principal como hicimos en el ejemplo anterior. Visita tu navegador e intenta buscar tu película favorita.
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.js
archivo en la src
carpeta y veamos cómo lograrlo.
En el DownshiftThree.js
archivo, 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 DownshiftThree
componente de clase donde representamos el Downshift/
componente. En la devolución de llamada que se le pasa, tenemos un botón donde pasamos la getToggleButtonProps
función. El botón contiene un operador ternario donde establecemos el contenido del botón en función de si el selectedBook
estado del componente está establecido.
A continuación, llamamos a la isOpen
propiedad 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 onChange
mé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 App
componente 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 src
directorio, crearemos dos archivos: DownshiftInputField.js
y 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.js
archivo. Vamos a crear un componente funcional en nuestro DownshiftInputField.js
archivo:
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 isOpen
propiedad 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.js
archivo:
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.js
archivo, 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 onChange
toma un segundo parámetro llamado stateAndHelpers
que nos brinda información sobre el estado actual de Downshift.
En el onChange
método, encontramos el campo de entrada con el que el usuario está interactuando actualmente obteniéndolo id
del stateAndHelpers
argumento 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 onSubmit
método obtiene el elemento seleccionado book
del movie
estado y hace lo que queramos con él.
Importa y renderiza el DownshiftFour/
componente en el App
componente 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-popper
paquete al configurar la aplicación, así que creemos un DownshiftFive.js
archivo en la src
carpeta.
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 DownshiftFive
clase 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 Manager
componente es un contenedor expuesto por Popper que necesita rodear a todos los demás react-popper
componentes de la página para que se comuniquen entre sí.
Si observas con atención, verás que estamos pasando una render
propiedad 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 render
propiedad.
En la devolución de llamada que se pasa a la render
propiedad, 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 selectedItem
a su placement
propiedad. 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 App
componente 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.
Deja una respuesta