Multi selector casero en React js.

Josué Acevedo Maldonado
8 min readSep 11, 2020

--

Piensa diferente.

Photo by Halacious on Unsplash

Durante el desarrollo de un pequeño proyecto, necesite el uso de un menú multiselector, sin embargo el diseño de los componentes existentes no me convencieron del todo.

Fuente: https://www.npmjs.com/package/react-multi-select-component.

Asi que decidí diseñar un componente con multi selección un poco más simple. Estaría conformado por un campo de texto, el cual permitiría escribir la palabra buscada en el submenú; el submenú en la parte superior del campo de texto, en el cual se mostrarán las opciones a elegir y por último en la parte inferior se ubicarán las opciones elegidas.

Fuente: elaboracion propia.

Por lo que comenzaremos creando una función en React js llamada Menuselector, además de la función HelloWorld, donde colocaremos la vista principal y lo agregaremos al doom de nuestro HTML con

ReactDOM.render(<HelloWorld/>,document.getElementById(‘root’))

En la función Menuselector comenzaremos agregando un campo de texto.

function Menuselector() { return (
<div className=”form-group”>
<input
type=”text”
className=”form-control”
role=”textbox”
placeholder=””
/>
</div>
);
}

Este será nuestro elemento principal, ya que gracias a el podremos filtrar los resultados del submenú, además de desplegar el submenú al dar clic sobre el mismo.

Ahora agregaremos el submenú el cual estará constituido por los siguientes elementos:

<div className=”mymenu “>
<div className=”tt-dataset”>
<div className=”tt-suggestion “ >
zapato
</div>
</div>
</div>

Para que se vea mejor nuestro componente agregaremos algo de css.

.form-group {margin-bottom: 20px;
position: relative;
width: 500px;
margin: auto;
}.mymenu {width: 100%;
position: absolute;
bottom: 70px;
display: block;
}.tt-suggestion {border-radius: 3px;
background-color: rgb(255, 255, 255);
z-index: 1000;
border: 0.1px solid rgb(221, 221, 221);
box-shadow: rgba(0, 0, 0, 0.176) 1px 4px 12px;
display: block;
padding: 8px 15px;
cursor: pointer;
}

Por lo que logramos que se vea de la siguiente forma:

Fuente: elaboracion propia.

Algo importante que destacar es que el atributo bottom: 70px; de la clase .mymenu le permite al submenú colocarse por arriba de la caja de texto y no intervenir son su visibilidad, además el atributo z-index:1000; de la clase .tt-suggestion le permite a la opción del submenú colocarse por encima del resto de los elementos HTML.

Ahora vamos a pasarle los datos a nuestro multiselector, usaremos un arreglo de objetos para trabajar con nuestras opciones, por lo que en la función HelloWorld que es donde instanciamos nuestro componente multiselector, crearemos el arreglo de objetos y se los pasaremos al componente.

const data = [
{‘id’:1,’nombre’:”zapato”}
,{‘id’:2,’nombre’:”tenis”}
];
<Menuselector
data={data}
/>

En la función Menuselector recibiremos los datos mediante el objeto props

function Menuselector(props) {
// Data
var options = props.data;

y los guardaremos en la variable options. Ya que tenemos los datos en el multiselector vamos recorrerlos con un map y mostrar las opciones presentes en el elemento .tt-suggestion

{options .map((item, index) => (
<div
className=”tt-suggestion”
key={item.id}
data-key={item.nombre}
value={item.id}
>
{item.nombre}
</div>
))}

La variable index refiere al índice del objeto dentro del arreglo y el atributo key es un elemento que requiere React en el virtual doom para distinguir a cada uno de los elementos. Al visualizar nuestro componente tenemos lo siguiente.

Fuente: elaboracion propia.

Ahora haremos que por defecto el submenú se encuentre oculto y al hacer clic en el input se muestren las opciones. Por lo tanto agregaremos un hook en nuestro componte que nos permita controlar el estado de visible o no visible.

const [visible, setvisible] = React.useState(false);

Agregaremos un style en el elemento .tt-suggestion

style={{display: `${visible ? “block” : “none”}`}}

Y las funciones que modificaran el valor del estado visible a razón de un par de eventos que describiré a continuación.

const cambiarestado = e => {
if (visible === false) {
mostrardatos(true);
}
}

La función cambiarestado determina si el menú es visible de lo contrario, invocará a la función mostrar datos con el valor true para que se haga visible.

const mostrardatos = input => {
setvisible(input);
document.addEventListener(‘click’, ocultardatos);
}

La función mostrardatos por su parte recibe un dato llamado input y le pasa ese valor a la función setvisible que es el que controla el cambio en la variable visible, además de esto, se crea un evento de clic que se asocia al documento HTML, el cual invoca a la función ocultardatos, esto lo hacemos con la intención de que el menú desplegable se oculte al dar click en cualquier parte del documento.

const ocultardatos = e => {
setvisible(false);
document.removeEventListener(‘click’, ocultardatos);
}

La función ocultardatos envía el valor false a la función setvisible y elimina el evento de clic que colocamos en el documento, ya que ha cumplido su misión.

Por último, agregaremos una etiqueta <a> al input para poder hacer clic sobre él y le asociaremos la función onClick que invoque a la función cambiarestado.

<a onClick={cambiarestado}>
<input
type=”text”
className=”form-control”
role=”textbox”
placeholder=””
/>
</a>

Asi ya tenemos la funcionalidad de despliegue del submenú.

Pasamos, por lo tanto, a la posibilidad de elegir un elemento y verlo en la parte inferior. Agregamos la variable de estado selección donde tendremos un arreglo con los objetos seleccionados.

const [seleccion, addelement] = React.useState([]);

En el elemento .tt-suggestion agregaremos el evento onClick el cual creara un nuevo objeto con los datos nombre e id, con los datos provenientes del elemento que desató el evento identificado como e. Usaremos la función getAttribute para acceder al campo data-key donde almacenamos el nombre en el elemento HTML. Ya que tenemos el objeto creado lo agregamos al arreglo llamado selección con la función concat() y se lo pasamos a addelement para que altere el valor del estado selección ya que concat() nos devuelve una copia de ese valor.

onClick={e => {
const nuevodato = {
name: e.target.getAttribute(‘data-key’)
, id: e.target.getAttribute(‘value’)
}
addelement(seleccion.concat(nuevodato))
}
}

Ya que tengamos los objetos almacenado en la variable llamada selección, vamos a mostrarlos en la parte inferior del componente. Usaremos un div y un botón de forma improvisada, emplearemos de nueva cuenta la función map.

<div>
{seleccion.map((item, index) => (
<button key={index}>
{item.name}
</button>
))}
</div>

Por lo que ahora, ya podemos ver los elementos que damos clic en el submenú, en la parte inferior del componente.

Fuente: elaboracion propia.

Antes de que se nos olvide, vamos a agregar la función de búsqueda mediante las palabras que escribiremos en el campo de texto, de tal forma que filtre las opciones mostradas en el submenú.

Añadimos de nueva cuenta un hook.

const [input, setinput] = React.useState(‘’);

Este hook almacenara el valor del input gracias a un evento Onchange.

<inputonChange={e => {
setinput(e.target.value);
cambiarestado(e) // al escribir en el input, se desplegaran
// las opciones
}}
type=”text”
className=”form-control”
role=”textbox”
placeholder=””
/>

Para posteriormente filtrar el arreglo de objetos al momento de visualizar los datos.

{options 
.filter(item => item.nombre.toUpperCase().search(input.toUpperCase()) !== -1)
.map((item, index) => (

Usamos la función toUpperCase() al momento de hacer el filtrado para que no importe mucho el como esta escrito el nombre del elemento a buscar o lo que escribamos en el input, de esta forma no será un preocupación.

Fuente: elaboracion propia.

Para eliminar los componentes seleccionado crearemos una función que llamaremos deleteelement y lo invocaremos al hacer clic sobre el boton que representa cada opción elegida.

<button onClick={deleteelement} value={index} key={index}
{item.name}
</button>

La función deleteelement, crea una copia del arreglo selección y obtiene el índice del objeto a eliminar dentro del arreglo selección; este dato lo recupera del atributo value, de existir el índice se elimina con la función splice() una vez hecho esto se carga el nuevo arreglo a la función addelement que modificar el estado de la variable selección.

const deleteelement = e => {
var array = […seleccion]; // make a separate copy of the array
const index = e.target.getAttribute(‘value’)
if (index !== -1) {
array.splice(index, 1);
addelement(array);
}
}

Y para terminar vamos a exportar los valores de este componente hacia el exterior del mismo, de tal forma que funcione de forma similar a un input o cualquier otro elemento HTML en Reactjs. En la función HelloWorld agregaremos un hook que almacene la selección del componente llamado selected.

const HelloWorld = () => {
const [selected, setSelected] = React.useState([]);

le pasaremos al componente el valor inicial guardado en selected y la función de actualización de ese estado llamado setSelected.

<Menuselector
data={data}
value={selected}
onChange={setSelected}
/>

Dentro del componente Menuselector, crearemos un par de variables que atrapen el valor inicial y la función ya mencionados.

function Menuselector(props) {
// Data
var options = props.data;
const onChange = props.onChange
const valueelement = props.value

Inicializaremos el valor de la variable selección con el valor que proviene del exterior.

const [seleccion, addelement] = React.useState(valueelement);

E invocaremos al evento onChange que almacena la función de cambio que fue traído del exterior del componente, en el momento en el que se agrega un nuevo objeto al arreglo al seleccionar una opción

onClick={e => {
const nuevodato = {
name: e.target.getAttribute(‘data-key’)
, id: e.target.getAttribute(‘value’)
}
addelement(seleccion.concat(nuevodato)) onChange(seleccion.concat(nuevodato));
}
}

y al eliminar un elemento.

if (index !== -1) {
array.splice(index, 1);
addelement(array);
onChange(array);
}

Gracias a esto, al consultar a la variable selected en la función HelloWorld obtendremos el arreglo de objetos seleccionados dentro de nuestro componente.

Pueden encontrar todo el codigo y el componente en funcionamiento en Codepen .

Siéntase con la libertad de modificarlo y crear sus propias versiones; además de agregar mas funcionalidades como el eliminar un elemento ya seleccionado del submenú para que no se pueda elegir nuevamente. Hasta pronto.

Fuente: elaboracion propia.

Si has notado algún error en este artículo o sientes que se puedes hacer mejoras, por favor házmelo saber en la sección de comentarios, ¡lo aprecio mucho!

Josue Acevedo Maldonado es ingeniero de software, trabaja actualmente como consultor.

Conectarse en LinkedIn.

¡Gracias por ser parte de la comunidad!
Puede encontrar contenido relacionado en el canal de YouTube, Twitter, Twitch, Spotify, etc, ademas del libro Ensamblador X86.

Finalmente, si ha disfrutado este artículo y siente que ha aprendido algo valioso, por favor comparta para que otros también puedan aprender de él.

¡Gracias por leer!

--

--

Josué Acevedo Maldonado
Josué Acevedo Maldonado

Written by Josué Acevedo Maldonado

Amante de la tecnologia y con pasion en resolver problemas interesantes, consultor, y creador del canal de youtube NEOMATRIX. https://linktr.ee/neomatrix

No responses yet