No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso de React Native CLI

Curso de React Native CLI

Alejandro Sanabria

Alejandro Sanabria

Eliminando favoritos

19/22
Recursos

Aportes 15

Preguntas 4

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

En mi caso, decidi colocar el botón de favoritos en el header del componente, así como usar una estrella en lugar de un botón.
Hice un pequeño post explicando como replicarlo si así lo desean, dejo una foto para que vean como se ve.

Me parece que la función removeFavorite sea async esta demás ya que la que ejecuta la parte asíncrona es el onPress de remove, yo le dejaria así y funciona bien:

removeFavorite() {
    Alert.alert('Remove favorite', 'Are you sure?', [
      {
        text: 'Cancel',
        onPress: () => {},
        style: 'cancel',
      },
      {
        text: 'Remove',
        onPress: async () => {
          const key = `favorite-${this.state.coin.id}`;
          const removed = await Storage.instance.remove(key);
          if (removed) {
            this.setState({isFavorite: false});
          }
        },
        style: 'destructive',
      },
    ]);
  }

En mi caso separe un poco las funciones 😃


 removeFavoriteAlert = () => {
    Alert.alert(
      `Remove From Favorites `,
      `Are you sure that you want to remove ${this.state.coin.name} from you favorites?`,
      [
        {
          text: 'Cancel',
          onPress: () => {},
          style: 'cancel',
        },
        {
          text: 'Remove',
          onPress: () => this.removeFavorite(),
          style: 'destructive',
        },
      ],
      { cancelable: false }
    );
  };
  removeFavorite = async () => {
    const key = `favorite-${this.state.coin.id}`;
    const favorite = await Storage.instance.remove(key);
    if (favorite) {
      this.setState({ isFavorite: false });
    }
  };

Así es como resolví el problema que tenia de no poder pasarle un callback como segundo parámetro al useState, como si se puede con el setState({}, callback) de la clases.

Si tienes una mejor solución déjala en los comentarios 😃👇

const getFavorite = async (coin) => {
    try {
      const key = `favorite=${coin.id}`;
      await Storage.instance.get(key).then((value) => {
      if(value){
        setIsFavorite(value)
      }
    })
    } catch (error) {
      console.log(`get favorites err ${error}`)
    }
  };

useEffect(() => {
    const {coin} = props.route.params;
    
    getFavorite(coin);
  }, []);

SI ESTAN TRABAJANDO CON HOOKS Y EL REMOVE FAVORITES NO LES FUNCIONA

Pase como parámetro la coin que luego recibirán en el useEffect(). Ya que si llaman directamente al estado en getFavorite() todas las criptos van a setearse en “Fav” debido a que la key se forma con el estado general.


Antes del bug

const getFavorite = async () => {
    try {
	// nótese como pasaba mi estado general directamente a la funcion
      const key = `favorite-${cryptoCoin.id}`;

      const favStr = await Storage.instance.get(key);

      if (favStr != null) {
        setIsFavorite(true);
      }
    } catch (error) {
      console.log('getFavorite error', error);
    }
  };

Arreglado el bug

const getFavorite = async (coin) => {
    try {
	//pasé como parámetro el coin que luego lo recibiré en el useEffect()
	// de esta forma individualizo los estados y ya funciona
      const key = `favorite-${coin.id}`;

      const favStr = await Storage.instance.get(key);

      if (favStr != null) {
        setIsFavorite(true);
      }
    } catch (error) {
      console.log('getFavorite error', error);
    }
  };

No me funciona el alert, pero tampoco salta un error, la librería de alert está debidamente importada. Al dar click se ejecuta el log de ELIMINADO, o cual quiere decir que si esta entrando a la función, pero no se por que no se ejecuta el otro pedazo de código.

¿Alguien sabe que puedo hacer?

Esto estaría genial fantástico con Hooks!

Creo que se puede optimizar más el código si el agregar y eliminar fav está dentro del toogle, además, siendo que las opciones del storage ya son asyn, considero que no es necesario que el Add y Remove lo sean:

    const toogleFavorite = () => {
        const key = `favorite-${coin.id}`
        if(!isFavorite){
            const coinFav = JSON.stringify(coin);
            const stored = Storage.instance.store(key, coinFav);
            (stored) && setIsFavorite(true);
        }else{
            Alert.alert(
                "Remove favorite",
                "Are you sure?",
                [{
                    text: "cancel",
                    onPress: () => {},
                    style: "cancel"
                },
                {
                    text: "Remove",
                    onPress: () => {
                        const removed = Storage.instance.remove(key);
                        (removed) && setIsFavorite(false);
                    },
                    style: "destructive"
                }]
            )
         }
    }

Se puede personalizar la alerta con estilos personalizados?

Por favor un guia con el siguiente error, hasta ayer todo estuvo correcto, me salió todo lo del vídeo, los mensajes de eliminado todo bien, pero hoy quice volver a compilar el proyecto y me sale los errores que transcribo:

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ‘:app:installDebug’.

com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: INSTALL_FAILED_INSUFFICIENT_STORAGE

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

  • Get more help at https://help.gradle.org

BUILD FAILED in 54s

error Failed to install the app. Make sure you have the Android development environment set up: https://reactnative.dev/docs/environment-setup. Run CLI with --verbose flag for more details.
Error: Command failed: ./gradlew app:installDebug -PreactNativeDevServerPort=8081
Unable to install /home/hector/Documentos/dev/cryptoTracker/android/app/build/outputs/apk/debug/app-debug.apk
[email protected]:~/Documentos/dev/cryptoTracker$

Hola amigos, ¿de que sirve el estilo de "style: ‘destructive’,?? por que por lo menos en Android el btn no se ve diferente.

para no repetir mucho lo del nombre de la key hice esta funcion get_key = () => { return `favorite-${this.state.coin.id}` }

Preferí dejar el Alert dentro de la función “toggleFavorite”, así la función de eliminar, queda más limpia:

const toggleFavorite = () => {
        if (isFavorite) {
            Alert.alert(
                'Remove Favorite',
                'Are you sure?',
                [
                    {
                        text: 'Cancel',
                        onPress: () => {},
                        style: 'cancel'
                    },
                    {
                        text: 'Remove',
                        onPress: removeFavorite,
                        style: 'destructive'
                    },
                ]
            )
        } else {
            addFavorite();
        }
    }

const removeFavorite = async () => {
        const key = `favorite-${coin.id}`;

        try {
            await Storage.instance.remove(key);
            setIsFavorite(false);
        } catch { }
    }

Excelente clase, no sabía que se podía hacer lo del setState con el callback, les comparto mi código 😃

import  React, {Component } from 'react';
import {View, Image, Text, StyleSheet, SectionList, Pressable, Alert} from 'react-native'
import Colors from '../../res/color'
import Http from '../../libs/http'
import CoinMarketItem from '../coinDetail/CoinMarketItem'
import Storage from '../../libs/storage'

import { FlatList } from 'react-native-gesture-handler';

class CoinDetailScreen extends Component {

    state = {
        coin:{},
        markets:[],
        isFavorite:false,
    }

    toggleFavorite = () =>{
      if(this.state.isFavorite){
        this.removeFavorite()
      }else{
        this.addFavorite()
      }
    }

    addFavorite = async () => {
      const coin = JSON.stringify(this.state.coin)
      const key = `favorite-${this.state.coin.id}`

      const stored = await Storage.instance.store(key,coin)

      console.log("stored",stored)

      if(stored){
        this.setState({isFavorite:true})
      }
    }
    removeFavorite = async() => {

      Alert.alert("Eliminar de favoritos", "¿Estas seguro?",[{
        text:'Cancelar',
        onPress: () => {},
        style:"cancel"
      },
      {
        text:'Eliminar ',
        onPress: async () => {
          const key = `favorite-${this.state.coin.id}`

          const stored = await Storage.instance.remove(key)
    
          console.log("remove",stored)
    
          if(stored){
            this.setState({isFavorite:false})
          }
        },
        style:"destructive"
      }
    ])
      

    }
    getFavorite = async () => {
      try {
        const key = `favorite-${this.state.coin.id}`
        const favStr = await Storage.instance.get(key)
        console.log("getFavorite",favStr)

        if(favStr != null){
          this.setState({isFavorite:true})
        }
      } catch (error) {
        console.log("get favorite error",error)
      }



      if(stored){
        this.setState({isFavorite:true})
      }
    }
    getSymbolIcon = (name) => {
        if (name) {
            const symbol = name.toLowerCase().replace(" ","-")
          return `https://c1.coinlore.com/img/16x16/${symbol}.png`;
        }
    };
    getSections = (coin) => {

        const sections = [
          {
            title: "Market cap",
            data: [coin.market_cap_usd]
          },
          {
            title: "Volume 24h",
            data: [coin.volume24]
          },
          {
            title: "Change 24h",
            data: [coin.percent_change_24h]
          }
        ];
    
        return sections;
    }

    getMarkets = async (coinId) => {

      const url = `https://api.coinlore.net/api/coin/markets/?id=${coinId}`
  
      const markets = await Http.instance.get(url);
  
      this.setState({ markets });
    }

    componentDidMount() {
        const {coin} = this.props.route.params;
        this.props.navigation.setOptions({title: coin.symbol})
        this.getMarkets(coin.id)
        //Como se asincrono se tiene que hacer de esta forma, despues de que estado de coin se seteo, con el callback de this.setState
        this.setState({coin},() => {
          this.getFavorite()
        })
    } 

    render(){
        const {coin,markets,isFavorite} = this.state;
        return(
            <View style={styles.container}>
                <View style={styles.subHeader}>
                  <View >
                    <Image style={styles.iconImg} source={{uri: this.getSymbolIcon(coin.name)}}></Image>
                    <Text style={styles.titleText}>
                        {coin.name}
                    </Text>
                  </View>
                    
                    <Pressable
                    onPress={this.toggleFavorite}
                    style={[
                      styles.btnFavorite,
                      isFavorite?
                      styles.btnFavoriteRemove :
                      styles.btnFavoriteAdd
                    ]}>
                      <Text style={styles.btnFavoriteText}>
                        {isFavorite ? "Eliminar favorito":"Agregar favorito"}
                      </Text>
                    </Pressable>
                </View >
                <SectionList 
                    style={styles.section}
                    sections={this.getSections(coin)} 
                    keyExtractor={(item,index) => item+index} 
                    renderItem={({item}) => 
                    <View style={styles.sectionItem}>
                        <Text style={styles.itemText}>{item}</Text>
                    </View>
                    } 
                    renderSectionHeader={({section: {title}}) =>
                    <View style={styles.sectionHeader}> 
                        <Text style={styles.sectionText}>{title}</Text>
                    </View>
                    } 
                    />
                    <Text style={styles.marketsTitle}>Markets</Text>

                    <FlatList
                      style={styles.list}
                      horizontal={true}
                      data={markets}
                      renderItem={({ item }) => <CoinMarketItem item={item} />}
                    />
            </View>
        )
    }
}
export default CoinDetailScreen

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: Colors.charade
  },
  row: {
    flexDirection: "row"
  },
  subHeader: {
    backgroundColor: "rgba(0, 0, 0, 0.1)",
    padding: 16,
    flexDirection: "row",
    justifyContent: "space-between"
  },
  titleText: {
    fontSize: 16,
    fontWeight: "bold",
    color: "#fff",
    marginLeft: 8
  },
  iconImg: {
    width: 25,
    height: 25
  },
  section: {
    maxHeight: 220
  },
  list: {
    maxHeight: 100,
    paddingLeft: 16
  },
  sectionHeader: {
    backgroundColor: "rgba(0,0,0, 0.2)",
    padding: 8
  },
  sectionItem: {
    padding: 8
  },
  itemText: {
    color: Colors.white,
    fontSize: 14
  },
  sectionText: {
    color: Colors.white,
    fontSize: 14,
    fontWeight: "bold"
  },
  marketsTitle: {
    color: Colors.white,
    fontSize: 16,
    fontWeight: "bold",
    marginBottom: 16,
    marginLeft: 16
  },
  btnFavorite: {
    padding: 8,
    borderRadius: 8
  },
  btnFavoriteText: {
    color: Colors.white
  },
  btnFavoriteAdd: {
    backgroundColor: Colors.picton
  },
  btnFavoriteRemove: {
    backgroundColor: Colors.carmine
  }
});