Curso de React.js 2017

Toma las primeras clases gratis

COMPARTE ESTE ARTÍCULO Y MUESTRA LO QUE APRENDISTE

Durante los próximos días, voy a ir haciendo tutoriales mostrando como he ido completando los diferentes retos del curso hasta completar la UI tal como se muestra en los diseños compartidos por Leonidas.

Captura de pantalla 2018-01-09 a las 7.18.28.png

En este primer tutorial, completaremos la funcionalidad de búscador de vídeos ya que durante el curso hicimos la UI y recogimos sus datos pero en ningún momento le dimos funcionalidad de buscador.
Lo primero que haremos es añadirle el icono al buscador, para esto crearemos un nuevo icono tal y como hicimos anteriormente en el curso, en la carpeta src/icons/components/search.js:

import React from 'react';
import Icon from './icon';
function Search(props) {
 return(
   <Icon {...props}>
       <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
       <path d="M0 0h24v24H0z" fill="none"/>
   </Icon>
 )
}
export default Search;

Ahora lo añadiremos a nuestro componente de búsqueda:
src/widgets/component/search.js

import React from 'react';
import './search.css';
import SearchSVG from '../../icons/components/search';
const Search = (props) => ( // si solo vas a devolver UI puedes hacerlo en forma de arrow sin usar return
 <form
   onSubmit={props.handleSubmit}
   className="Search"
 >
   <input className="Search-input"
     ref={props.setRef}
     placeholder="Busca tu video favorito"
     type="text"
     name="search"
     // defaultValue="Luis Fonsi"
     onChange={props.handleChange}
     value={props.value}
   />
   <span className="Search-icon">
     <SearchSVG  size={35} color="#6f737c"/>
   </span>
 </form>
)
export default Search;

añadiremos al archivo css la clase Search-icon para ajustar su posición y haremos unos ajustes en el css del buscador, ya que en el diseño original se ve más estrecho que como lo dejamos durante el curso.
src/widgets/component/search.css


.Search {
   max-width: 400px;
   margin-bottom: 20px;
   position: relative;
 }

 .Search-input {
   background: #f2f2f2;
   padding: 10px;
   font-size: 18px;
   font-family: Arial;
   width: 100%;
   border-radius: 10px;
   border:none;
   outline: 0;
   box-shadow: 2px 2px 5px rgba(0,0,0,.2);
 }

 .Search-input:focus {
   box-shadow: 0 0 10px rgba(0,0,0,.5);
 }


 .Search-icon {
   position: absolute;
   right: -15px;
   top: 10px;
 }

Bien, ahora ya se asemeja más a lo que teníamos en el diseño, vamos a agregarle funcionalidad de buscador que es lo realmente interesante.
Lo primero que haremos será añadir al estado de nuestro container principal (src/pages/containers/home.js) los atributos inputValue ( para recoger lo que escribamos en el input, lo inicializamos como string vacío) y searched ( donde haremos el filtrado de medias que hagamos a través del input y lo iniciaremos como un array vacío):
src/pages/container/home.js

state = {
   modalVisible: false,
   inputValue: '',
   searched: []
 }

también crearemos una función que se encarge de filtrar los medias de nuestra “API”:

handleInputChange = event => {
   let searchedMedia = {"playlist": []}
   this.props.data.categories.forEach(category => {
     category.playlist.forEach(media => {
         if (media.title.toUpperCase().indexOf(event.target.value.toUpperCase()) >= 0 || media.author.toUpperCase().indexOf(event.target.value.toUpperCase()) >= 0) {
           searchedMedia.playlist.push(media);
         }
       })
       return searchedMedia
   })
   this.setState({
     inputValue: event.target.value,
     categories: searchedMedia
   })

En esta función lo que hacemos es lo siguiente, inicializamos una variable a la que le asignamos un json con la clave playlist que de momento contendrá un array vacío (aquí rellenaremos los resultados de nuestra búsqueda para luego recorrerlo y mostrarlo en pantalla).

let searchedMedia = {"playlist": []}

después recorreremos el array de categories que es el que contiene nuestras playlists, que este a su vez es otro array que contiene nuestras medias que es a donde queremos llegar:

this.props.data.categories.forEach(category => {
     category.playlist.forEach(media => {
    
   })

Una vez estamos recorriendo cada una de nuestras playlist comprobaremos si el título o el autor del media coinciden con nuestra búsqueda, para esto lo primero que haremos será hacer una transformación a mayúscula o minúscula del string que contiene el título, el nombre del autor y lo introducido en el input, esto lo haremos para que se puedan encontrar coincidencias sin importar que hayan sido introducidas en mayúsculas o minúsculas en el Input, después utilizaremos la función indexOf (esta función devuelve un número indicando en qué posición del string se encuentra la cadena buscada, si esa cadena no coincide devolvería un -1, por lo que si es mayor a 0 significa que hay coincidencia!!), si hay coincidencia haremos push de ese media al array (playlist) definido en la variable searchedMedia :

     if (media.title.toUpperCase().indexOf(event.target.value.toUpperCase()) >= 0 || media.author.toUpperCase().indexOf(event.target.value.toUpperCase()) >= 0) {
           searchedMedia.playlist.push(media);
         }
       })
       return searchedMedia

Después de esto “setearemos” los datos al estado del container home para poder enviarselo a nuestro componente de búsqueda y futuro componente de render:

this.setState({
     // value: event.target.value.replace(' ','-')
     inputValue: event.target.value,
     categories: searchedMedia
   })

por último en el container Home.js, enviaremos estos datos al componente categories que es el que contiene tanto nuestro Search component como el componente encargado de renderizar medias.

<Categories
           handleInputChange={this.handleInputChange}
           categories={this.props.data.categories}
           handleOpenModal={this.handleOpenModal}
           inputValue={this.state.inputValue}
           searched={this.state.searched}
         />

Dentro de categories le añadiremos estas propiedades a nuestro componente de search:
src/categories/components/categories.js

<Search handleInputChange={props.handleInputChange} inputValue={props.inputValue}/>

en el container de search modificaremos las funciones creadas en el por las nuevas implementadas en el home container (No estoy seguro si es mejor práctica definir las funciones en el mismo container de search o en el contenedor principal, estaré encantado de leer la opinión de los expertos 😄):
src/widget/containers/search.js

<Search
       setRef={this.setInputRef}
       handleSubmit={this.handleSubmit}
       handleChange={this.props.handleInputChange}
       value={this.props.inputValue}
     />

Bien, en este punto tenemos todo lo necesario para poder enviarle a un componente los datos de las medias para que las renderice, pero falta lo más importante ¿que componente será el encargado de hacer este renderizado?, aquí hay varias posibilidades, usar el mismo componente de categories y enviarle el nuevo array de medias (habría que cambiar este array pero sería posible hacerlo) en mi caso decidí crear un nuevo componente y renderizarlo en categories dependiendo de si hay busquedas.

src/playlist/components/searched-media.js:

import React from 'react';
import './searched-media.css';
import Media from './media';
function SearchedMedia(props) { 
 return(
   <div className="SearchedMedia">
     <h2 className="SearchTitle">Resultados de la busqueda: </h2>
     {
       props.playlist.map(item => {
        return  <div className="SearchedItem" key={item.id}>
                   <Media openModal={props.handleOpenModal} {...item} />
                 </div>
       })
     }
   </div>
 )
}
export default SearchedMedia;

Este es un componente muy sencillo, el que recibirá por props los medias a renderizar y se los enviará a nuestro componente Media que creamos a lo largo del curso.
os dejo también los estilos que le puse a este componente:
src/playlist/components/search-media.css

.SearchedMedia {
   max-width: 100%;
   height: 100vh;
   overflow-y: scroll;
}

.SearchedItem{
   padding-bottom: 80px;
   width: 260px;
   float:left;
   margin: 0 15px 5px 0;
}

.SearchTitle {
 text-transform: uppercase;
 font-size: 20px;
 color: #3f546c;
 margin: 0 0 20px 0;
 letter-spacing: 2px;
 font-weight: bold;
}

ahora solo queda que añadamos este componente a Categories y decirle que cuando haya una búsqueda renderice el nuevo componente:
src/categories/components/categories.js

import React from 'react';
import Category from './category';
import './categories.css';
import Search from '../../widgets/containers/search';
import SearchedMedia from '../../playlist/components/searched-media';
function Categories(props) {
 return (
   <div className="Categories">
   <Search handleInputChange={props.handleInputChange} inputValue={props.inputValue}/>
   { !props.inputValue ?
     props.categories.map(categoria => {
       return (
         <Category
           key={categoria.id}
           {...categoria}
           handleOpenModal={props.handleOpenModal}
         />
       )
     })
     :
     <SearchedMedia
       handleOpenModal={props.handleOpenModal}
       {...props.searched}
     />
   }
   </div>
 )
}

export default Categories;

Y con esto ya hemos implementado la funcionalidad de buscador.
Me gustaría que compartierais vuestras formas de solventarlo y si hay alguna cosa que consideréis que debería corregir, también agradecería que me dijerais cosas a mejorar en mis tutoriales o si debo mejorar algún aspecto o lo que sea ya que es mi primer tutorial.
os dejo el repositorio de Github con este reto solventado:
https://github.com/jalcantara90/platzi-video/tree/buscador

saludos a todos.

Curso de React.js 2017

Toma las primeras clases gratis

COMPARTE ESTE ARTÍCULO Y MUESTRA LO QUE APRENDISTE

0 Comentarios

para escribir tu comentario

Artículos relacionados