CursosEmpresasBlogLiveConfPrecios

Debuggeando nuestro proyecto (agregando validaciones a nuestro componente card)

Clase 15 de 29 • Curso de React Router 5 y Redux

Clase anteriorSiguiente clase

Contenido del curso

Bienvenida al curso

  • 1
    Todo lo que aprenderás sobre React Router y Redux

    Todo lo que aprenderás sobre React Router y Redux

    01:30 min

¿Qué es React Router? y Aplicarlo en tus proyectos

  • 2
    ¿Qué es React Router y cómo instalarlo?

    ¿Qué es React Router y cómo instalarlo?

    02:54 min
  • 3
    Crear nuestro archivo de Rutas

    Crear nuestro archivo de Rutas

    07:33 min
  • 4
    Container: Login

    Container: Login

    10:29 min
  • 5
    Container: Register

    Container: Register

    06:28 min
  • 6
    Container: 404 Not Found

    Container: 404 Not Found

    06:43 min
  • 7
    Componente: Layout

    Componente: Layout

    05:10 min
  • 8
    Manejando enlaces y configuraciones

    Manejando enlaces y configuraciones

    06:33 min

¿Qué es Redux?

  • 9
    Qué es Redux

    Qué es Redux

    02:17 min
  • 10

    ¿Qué es Redux? Profundizando en la herramienta

    01:32 min
  • 11
    Instalación de Redux

    Instalación de Redux

    04:40 min
  • 12
    Creando el Store de Redux

    Creando el Store de Redux

    12:53 min

Aplicando Redux a nuestra aplicación

  • 13
    Creando los reducers

    Creando los reducers

    10:40 min
  • 14
    Finalizando reducers y eliminar favoritos

    Finalizando reducers y eliminar favoritos

    08:26 min
  • 15
    Debuggeando nuestro proyecto (agregando validaciones a nuestro componente card)

    Debuggeando nuestro proyecto (agregando validaciones a nuestro componente card)

    Viendo ahora
  • 16
    Crear Formulario de Login

    Crear Formulario de Login

    10:08 min
  • 17
    Formulario de Login con Redux

    Formulario de Login con Redux

    06:28 min
  • 18
    Creando un Servicio para Gravatar

    Creando un Servicio para Gravatar

    05:28 min
  • 19
    Uso de gravatar en nuestro proyecto

    Uso de gravatar en nuestro proyecto

    07:16 min
  • 20
    Validación para LogIn LogOut

    Validación para LogIn LogOut

    09:15 min
  • 21
    Register

    Register

    07:00 min
  • 22
    Register con Redux

    Register con Redux

    04:17 min
  • 23
    Vista general del player

    Vista general del player

    05:26 min
  • 24
    Arreglando la funcionalidad del player

    Arreglando la funcionalidad del player

    06:55 min
  • 25
    Terminando de detallar nuestro player

    Terminando de detallar nuestro player

    13:50 min
  • 26
    Validaciones

    Validaciones

    10:35 min
  • 27
    Validaciones de UI

    Validaciones de UI

    07:04 min
  • 28
    Debug con Redux Devtools

    Debug con Redux Devtools

    05:57 min

Cierre del curso

  • 29
    Cierre del Proyecto

    Cierre del Proyecto

    00:39 min
  • Tomar el examen del curso
    • Jesús Miguel Quinto Teran

      Jesús Miguel Quinto Teran

      student•
      hace 6 años

      Si quieres evitar que el mismo elemento se agregue muchas veces a la lista de favoritos:

      reducers/index.js

      case 'SET_FAVORITE': const exist = state.mylist.find(item => item.id === action.payload.id) if (exist) return {...state} return { ...state, mylist: [...state.mylist, action.payload] }
        Juan Jose Vega

        Juan Jose Vega

        student•
        hace 6 años

        Fácil y rápida, top

        David Dias

        David Dias

        student•
        hace 5 años

        Justo estaba pensando en una solución a esto. Gracias!

      Asis Melgarejo Lopez

      Asis Melgarejo Lopez

      student•
      hace 6 años

      Hay otro bug y es que una vez se agrega una película a la lista de mis favoritos ya no debe existir la opción de añadirla nuevamente.

      Recordemos que JS nos permite modificar los estilos de los elementos HTML Para solucionar este bug agregue unas lineas en el método handleSetFavorite y **handleDeleteFavorite **, los cuales ahora removerán y agregaran el display de none, respectivamente.

      const { id, cover, title, year, contentRating, duration, isList } = props; const handleSetFavorite = () => { props.setFavorite({ id, cover, title, year, contentRating, duration }); document.getElementById(id).style.display = 'none'; };
      const handleDeleteFavorite = (itemId) => { props.deleteFavorite(itemId); document.getElementById(id).style.display = 'inline'; };

      Adicionalmente, debemos agregar el atributo ID en cada imagen (plus and delete icon):

      {isList ? ( <img className='carousel-item__details--img' src={removeIcon} alt='Remove Icon' onClick={() => handleDeleteFavorite(id)} /> ) : ( <img className='carousel-item__details--img' src={plusIcon} alt='Plus Icon' onClick={handleSetFavorite} id={id} /> )}
        Jair Balcazar Cobeña

        Jair Balcazar Cobeña

        student•
        hace 6 años

        Me gusto mucho tu solución, muy elegante

        Juan Jose Vega

        Juan Jose Vega

        student•
        hace 6 años

        Qué tal la solución de @Jesuskinto es sencilla y funcional

      Pablo Domínguez Durán

      Pablo Domínguez Durán

      student•
      hace 6 años

      Otro bug que también hay (no sé si lo corrijan después) es que puedes agreagar el mismo video varias veces al myList, y se muestra varias veces.

      La propia consola te manda un error que el key se repite

        Hernán Giraldo

        Hernán Giraldo

        student•
        hace 5 años

        Para evitar ese bug usé un condicional ternario en el reducer para validar si el elemento existe, en caso de existir retorno el mismo estado anterior y en caso de que no, lo agrego:

        mylist: state.mylist.filter(i => i.id === action.payload.id).length === 0 ? [ ...state.mylist, action.payload] : [ ...state.mylist ]```
        Marco Alejandro Rodriguez Ferrer

        Marco Alejandro Rodriguez Ferrer

        student•
        hace 5 años

        Yo lo solucione con este codigo:

        reducer.png

      José Daniel Gómez Larin

      José Daniel Gómez Larin

      student•
      hace 6 años
      aporte1.PNG

      Para resolver el bug de ya no volver agregar el mismo item lo resolví así.

      Alejandro González Reyes

      Alejandro González Reyes

      student•
      hace 6 años

      Existe otro bug, un item añadido actualmente a la lista, no puede volver a añadirse

        Oscar Barajas Tavares

        Oscar Barajas Tavares

        Team Platzi•
        hace 6 años

        Seria un gran reto resolverlo. Te invito a compartirnos tu solución.

        David Camacho

        David Camacho

        student•
        hace 6 años

        En el reducer...

        case 'SET_FAVORITE': const itemId = state.myList.find(items => items.id === action.payload.id) if(itemId) return state; return { ...state, myList: [...state.myList, action.payload] }
      Carmen Sánchez Salgado

      Carmen Sánchez Salgado

      student•
      hace 6 años

      He visto varias soluciones para el bug de "agregar una pelicula más de una vez a la lista de favoritas".

      Esta es mi propuesta. Busco el índice (findIndex) del item que se va a agregar dentro de la lista myList. Si el índice no existe (-1) agrego el payload, sino, regreso el state original,

      case 'SET_FAVORITE': const index = state.myList.findIndex(items =&gt; items.id == action.payload.id); if(index == -1) return { ...state, myList: [...state.myList, action.payload] } return state;
        Jesús Adrián Arroyo Ceja

        Jesús Adrián Arroyo Ceja

        student•
        hace 6 años

        Dejo mi solución, un poco más simple

        case 'SET_FAVORITE': return { ...state, myList: [...state.myList].find((item) =&gt; item.id === action.payload.id) ? state.myList : [...state.myList, action.payload], };
      Tomas Goldenberg

      Tomas Goldenberg

      student•
      hace 6 años
      3 CREANDO VALIDACION PARA NO REPETIR VIDEO EN MYLIST Y SWEETALERT.jpg
        Camilo Morales Sanchez

        Camilo Morales Sanchez

        student•
        hace 5 años

        amigo me podrias enseñar como implementar esa alerta tan vacana, me podrias explicar como lo hago es que soy algo nuevo en esto y apenas estoy aprendiendo...

        te lo agradeceria

        Jimmy Buriticá Londoño

        Jimmy Buriticá Londoño

        student•
        hace 5 años

        La alerta en ese formato la obtienes en Sweet Alert, dar clic en el siguiente enlace:

        Sweet Alert

      Yancarlo Urdaneta

      Yancarlo Urdaneta

      student•
      hace 6 años

      Si jugaron un poco con las cartas del video, habrán visto que puedes agregar dos veces el mismo favorito. Para solucionarlo, hice esto. Declaré una constante fuera del switch, que se encargará de saber si existe o no, y coloqué la condicion en el return.

      const exists = state.mylist.find((item) => item.id === action.payload.id);

      Y el return de ** SET_FAVORITE ** es

      return exists ? state : { ...state, mylist: [...state.mylist, action.payload], };
      Arantxa Giovanna Rosas Del Valle

      Arantxa Giovanna Rosas Del Valle

      student•
      hace 6 años

      Yo resolví así el bug de no añadir más de una vez el mismo elemento

      case 'SET_FAVORITE': return state.myList.some((items) => items.id === action.payload.id) ? { ...state } : { ...state, myList: [...state.myList, action.payload], };
      Erik Ricardo Sánchez Pérez

      Erik Ricardo Sánchez Pérez

      student•
      hace 6 años

      Me parece que para la validación de si el elemento existe quedaría más simple de esta forma

      case 'SET_FAVORITE': const item = state.myList.find( list => list.id === action.payload.id ) if(item) return state; return { ...state, myList: [...state.myList, action.payload] }
      Vicente Daniel Zurita Salas

      Vicente Daniel Zurita Salas

      student•
      hace 6 años

      Yo modifiqué el reducer SET_FAVORITE.

      case 'SET_FAVORITE': if (state.myList.filter((element) => element.id == action.payload.id).length !== 0) { return state; } return { ...state, myList: [...state.myList, action.payload], };
      Andrés Campuzano Garzón

      Andrés Campuzano Garzón

      student•
      hace 6 años

      🦄 Acá el código realizado hasta el momento: https://github.com/AndresCampuzano/React-Router-and-Redux/commit/392489519d93f8d055167d7f4089560ef86c3b4f

      Marco Alejandro Rodriguez Ferrer

      Marco Alejandro Rodriguez Ferrer

      student•
      hace 5 años

      Para evitar agregar el mismo elemento varias veces a la lista de favoritos se puede usar en reducer/index.js, lo siguiente:

      reducer.png

      yeccid emmanuel amaya maldonado

      yeccid emmanuel amaya maldonado

      student•
      hace 5 años

      Este Profesor sin duda está entre los mejores de Platzi, no lo dejen ir y que por favor haga mas cursos

      Juan Pablo Leyva

      Juan Pablo Leyva

      student•
      hace 5 años

      Se sigue añadiendo infinitamente desde trends. Para arreglarlo en src/reducer/index.js

      const exists = state.myList.find((item) => item.id === action.payload.id); exists; return exists ? { ...state } : { ...state, myList: [...state.myList, action.payload], };```
      Iair Poloniecki

      Iair Poloniecki

      student•
      hace 6 años

      Yo lo hice así:

      case 'SET_FAVORITE': return state.mylist.find(item => item.id === action.payload.id) ? state : { ...state, mylist: [...state.mylist, action.payload], };
      Alejandro Betancur

      Alejandro Betancur

      student•
      hace 6 años

      Existe un "bug " , porque no necesariamente se puede ver así y es que si añades un elemento a los favoritos , no debería mostrarse en la lista de trends o de originals . Lo resoli así :

      import { actions } from "../actions/index"; const reducer = (state, action) => { switch (action.type) { case actions.setFavorite: if (state.trends.find(items => items.id === action.payload.id)) { return { ...state, trends: state.trends.filter(items => items.id !== action.payload.id), mylist: [...state.mylist, action.payload], } } else { return { ...state, originals: state.originals.filter(items => items.id !== action.payload.id), mylist: [...state.mylist, action.payload], } } case actions.deleteFavorite: return { ...state, mylist: state.mylist.filter(items => items.id !== action.payload), trends: [...state.trends, action.payload] } default: return state; } }; export default reducer;
      Rafa Jiménez

      Rafa Jiménez

      student•
      hace 6 años

      Para solucionar el bug que permitía agregar el mismo item varias veces a 'Mi lista', he creado estas dos funciones en el Home.jsx justo antes del return del componente, una para trends y otra para originals, lo que hacen es comparar con myList y sacar los elementos repetidos

      const trendsWhitOutMyList = trends.filter( ({ id: id1 }) => !myList.some(({ id: id2 }) => id2 === id1), ); const originalsWhitOutMyList = originals.filter( ({ id: id1 }) => !myList.some(({ id: id2 }) => id2 === id1), );

      Ademas hay que cambiar los map

      <Categories title='Tendencias'> <Carousel> {trendsWhitOutMyList.map((item) => ( <CarouselItem key={item.id} {...item} /> ))} </Carousel> </Categories> <Categories title='Originales de Platzi Video'> <Carousel> {originalsWhitOutMyList.map((item) => ( <CarouselItem key={item.id} {...item} /> ))} </Carousel> </Categories>```
        César Ernesto Rivas Martínez

        César Ernesto Rivas Martínez

        student•
        hace 6 años

        Hola Rafa,

        Esta solución solventa el problema pero considero que no es la mejor, ahora te explico por qué.

        Si te pones a pensar, el tener una nueva lista de videos (ya sea una, dos o mas), implica que tienes que hacer una nueva función para evitar que esta/s nueva/s lista/s muestren los videos que ya se agregaron a mi lista, y así sucesivamente. Esto agrega un nivel de complejidad innecesaria a nuestra aplicación.

        Saludos.

      Hans Elías Martínez Haar

      Hans Elías Martínez Haar

      student•
      hace 5 años

      Esta es otra forma de evitar que dos elementos se agreguen a la lista dos veces

      if(check[0] !== undefined ){ let band band = false for( let i = 0; i < Object.keys(check).length ; i++){ console.log (check[i].id , action.payload.id) if(check[i].id === action.payload.id){ band = true } if(band === true){ return state } band = false } }
      Gabriel augusto Rotger

      Gabriel augusto Rotger

      student•
      hace 5 años

      Hola, me confundió un poco saber como funciona isList. Es cada uno de los Items del carrusel?.

        ANTHONY JEAMPIER RANGEL RODRIGUEZ

        ANTHONY JEAMPIER RANGEL RODRIGUEZ

        student•
        hace 5 años

        isList es una bandera (Booleano) que nos va a permitir identificar a que lista pertenece el item que se esta enviando, si te das cuenta el isList solo se envia en esta seccion:

        <Categories title="Mi Lista"> <Carousel> {myList.map((item) => ( <CarouselItem key={item.id} {...item} isList /> ))} </Carousel> </Categories>

        pero en las otras secciones no se envía, como se ve acontinuacion:

        <Categories title="Tendencias"> <Carousel> {trends.map((item) => ( <CarouselItem key={item.id} {...item} /> ))} </Carousel> </Categories> <Categories title="Originales de Platzi Video"> <Carousel> {originals.map((item) => ( <CarouselItem key={item.id} {...item} /> ))} </Carousel> </Categories>

        lo que quiere decir, que en esas secciones el isList será null y en su defecto false. Por esa razón el icono eliminar solo aparece en la lista de favoritos y en las demás aparece el icono añadir. como se muestra en las siguientes imagenes.

        miLista.png
        tendencias.png

        Espero haberte ayudado.

        Rodrigo Acevedo Gutiérrez

        Rodrigo Acevedo Gutiérrez

        student•
        hace 5 años

        Hola, isList es una propiedad booleana (true o false) que solo se manda en el carrusel de favoritos, cuando solo se pasa su nombre la propiedad es "true" y cuando no se escribe es "false" , como solo se manda en el carrusel de favoritos en al condición ternaria se indica que si es "true" aparezca el icono de borrar, de lo contrario aparezca el icono de agregar (quiere decir que el elemento está en otro carrusel distinto al de favoritos)

    Escuelas

    • Desarrollo Web
      • Fundamentos del Desarrollo Web Profesional
      • Diseño y Desarrollo Frontend
      • Desarrollo Frontend con JavaScript
      • Desarrollo Frontend con Vue.js
      • Desarrollo Frontend con Angular
      • Desarrollo Frontend con React.js
      • Desarrollo Backend con Node.js
      • Desarrollo Backend con Python
      • Desarrollo Backend con Java
      • Desarrollo Backend con PHP
      • Desarrollo Backend con Ruby
      • Bases de Datos para Web
      • Seguridad Web & API
      • Testing Automatizado y QA para Web
      • Arquitecturas Web Modernas y Escalabilidad
      • DevOps y Cloud para Desarrolladores Web
    • English Academy
      • Inglés Básico A1
      • Inglés Básico A2
      • Inglés Intermedio B1
      • Inglés Intermedio Alto B2
      • Inglés Avanzado C1
      • Inglés para Propósitos Específicos
      • Inglés de Negocios
    • Marketing Digital
      • Fundamentos de Marketing Digital
      • Marketing de Contenidos y Redacción Persuasiva
      • SEO y Posicionamiento Web
      • Social Media Marketing y Community Management
      • Publicidad Digital y Paid Media
      • Analítica Digital y Optimización (CRO)
      • Estrategia de Marketing y Growth
      • Marketing de Marca y Comunicación Estratégica
      • Marketing para E-commerce
      • Marketing B2B
      • Inteligencia Artificial Aplicada al Marketing
      • Automatización del Marketing
      • Marca Personal y Marketing Freelance
      • Ventas y Experiencia del Cliente
      • Creación de Contenido para Redes Sociales
    • Inteligencia Artificial y Data Science
      • Fundamentos de Data Science y AI
      • Análisis y Visualización de Datos
      • Machine Learning y Deep Learning
      • Data Engineer
      • Inteligencia Artificial para la Productividad
      • Desarrollo de Aplicaciones con IA
      • AI Software Engineer
    • Ciberseguridad
      • Fundamentos de Ciberseguridad
      • Hacking Ético y Pentesting (Red Team)
      • Análisis de Malware e Ingeniería Forense
      • Seguridad Defensiva y Cumplimiento (Blue Team)
      • Ciberseguridad Estratégica
    • Liderazgo y Habilidades Blandas
      • Fundamentos de Habilidades Profesionales
      • Liderazgo y Gestión de Equipos
      • Comunicación Avanzada y Oratoria
      • Negociación y Resolución de Conflictos
      • Inteligencia Emocional y Autogestión
      • Productividad y Herramientas Digitales
      • Gestión de Proyectos y Metodologías Ágiles
      • Desarrollo de Carrera y Marca Personal
      • Diversidad, Inclusión y Entorno Laboral Saludable
      • Filosofía y Estrategia para Líderes
    • Diseño de Producto y UX
      • Fundamentos de Diseño UX/UI
      • Investigación de Usuarios (UX Research)
      • Arquitectura de Información y Usabilidad
      • Diseño de Interfaces y Prototipado (UI Design)
      • Sistemas de Diseño y DesignOps
      • Redacción UX (UX Writing)
      • Creatividad e Innovación en Diseño
      • Diseño Accesible e Inclusivo
      • Diseño Asistido por Inteligencia Artificial
      • Gestión de Producto y Liderazgo en Diseño
      • Diseño de Interacciones Emergentes (VUI/VR)
      • Desarrollo Web para Diseñadores
      • Diseño y Prototipado No-Code
    • Contenido Audiovisual
      • Fundamentos de Producción Audiovisual
      • Producción de Video para Plataformas Digitales
      • Producción de Audio y Podcast
      • Fotografía y Diseño Gráfico para Contenido Digital
      • Motion Graphics y Animación
      • Contenido Interactivo y Realidad Aumentada
      • Estrategia, Marketing y Monetización de Contenidos
    • Desarrollo Móvil
      • Fundamentos de Desarrollo Móvil
      • Desarrollo Nativo Android con Kotlin
      • Desarrollo Nativo iOS con Swift
      • Desarrollo Multiplataforma con React Native
      • Desarrollo Multiplataforma con Flutter
      • Arquitectura y Patrones de Diseño Móvil
      • Integración de APIs y Persistencia Móvil
      • Testing y Despliegue en Móvil
      • Diseño UX/UI para Móviles
    • Diseño Gráfico y Arte Digital
      • Fundamentos del Diseño Gráfico y Digital
      • Diseño de Identidad Visual y Branding
      • Ilustración Digital y Arte Conceptual
      • Diseño Editorial y de Empaques
      • Motion Graphics y Animación 3D
      • Diseño Gráfico Asistido por Inteligencia Artificial
      • Creatividad e Innovación en Diseño
    • Programación
      • Fundamentos de Programación e Ingeniería de Software
      • Herramientas de IA para el trabajo
      • Matemáticas para Programación
      • Programación con Python
      • Programación con JavaScript
      • Programación con TypeScript
      • Programación Orientada a Objetos con Java
      • Desarrollo con C# y .NET
      • Programación con PHP
      • Programación con Go y Rust
      • Programación Móvil con Swift y Kotlin
      • Programación con C y C++
      • Administración Básica de Servidores Linux
    • Negocios
      • Fundamentos de Negocios y Emprendimiento
      • Estrategia y Crecimiento Empresarial
      • Finanzas Personales y Corporativas
      • Inversión en Mercados Financieros
      • Ventas, CRM y Experiencia del Cliente
      • Operaciones, Logística y E-commerce
      • Gestión de Proyectos y Metodologías Ágiles
      • Aspectos Legales y Cumplimiento
      • Habilidades Directivas y Crecimiento Profesional
      • Diversidad e Inclusión en el Entorno Laboral
      • Herramientas Digitales y Automatización para Negocios
    • Blockchain y Web3
      • Fundamentos de Blockchain y Web3
      • Desarrollo de Smart Contracts y dApps
      • Finanzas Descentralizadas (DeFi)
      • NFTs y Economía de Creadores
      • Seguridad Blockchain
      • Ecosistemas Blockchain Alternativos (No-EVM)
      • Producto, Marketing y Legal en Web3
    • Recursos Humanos
      • Fundamentos y Cultura Organizacional en RRHH
      • Atracción y Selección de Talento
      • Cultura y Employee Experience
      • Gestión y Desarrollo de Talento
      • Desarrollo y Evaluación de Liderazgo
      • Diversidad, Equidad e Inclusión
      • AI y Automatización en Recursos Humanos
      • Tecnología y Automatización en RRHH
    • Finanzas e Inversiones
      • Fundamentos de Finanzas Personales y Corporativas
      • Análisis y Valoración Financiera
      • Inversión y Mercados de Capitales
      • Finanzas Descentralizadas (DeFi) y Criptoactivos
      • Finanzas y Estrategia para Startups
      • Inteligencia Artificial Aplicada a Finanzas
      • Domina Excel
      • Financial Analyst
      • Conseguir trabajo en Finanzas e Inversiones
    • Startups
      • Fundamentos y Validación de Ideas
      • Estrategia de Negocio y Product-Market Fit
      • Desarrollo de Producto y Operaciones Lean
      • Finanzas, Legal y Fundraising
      • Marketing, Ventas y Growth para Startups
      • Cultura, Talento y Liderazgo
      • Finanzas y Operaciones en Ecommerce
      • Startups Web3 y Blockchain
      • Startups con Impacto Social
      • Expansión y Ecosistema Startup
    • Cloud Computing y DevOps
      • Fundamentos de Cloud y DevOps
      • Administración de Servidores Linux
      • Contenerización y Orquestación
      • Infraestructura como Código (IaC) y CI/CD
      • Amazon Web Services
      • Microsoft Azure
      • Serverless y Observabilidad
      • Certificaciones Cloud (Preparación)
      • Plataforma Cloud GCP

    Platzi y comunidad

    • Platzi Business
    • Live Classes
    • Lanzamientos
    • Executive Program
    • Trabaja con nosotros
    • Podcast

    Recursos

    • Manual de Marca

    Soporte

    • Preguntas Frecuentes
    • Contáctanos

    Legal

    • Términos y Condiciones
    • Privacidad
    • Tyc promociones
    Reconocimientos
    Reconocimientos
    Logo reconocimientoTop 40 Mejores EdTech del mundo · 2024
    Logo reconocimientoPrimera Startup Latina admitida en YC · 2014
    Logo reconocimientoPrimera Startup EdTech · 2018
    Logo reconocimientoCEO Ganador Medalla por la Educación T4 & HP · 2024
    Logo reconocimientoCEO Mejor Emprendedor del año · 2024
    De LATAM conpara el mundo
    YoutubeInstagramLinkedInTikTokFacebookX (Twitter)Threads