How to render asynchronous content in React Native?

Asked

Viewed 365 times

1

I’m new to React Native and I’m developing a test application where I need to save some data on the device using Async Storage.

I was able to save the data correctly, but when I try to get the data to use in my component I cannot, because there is an error that it is not possible to render an object in a jsx chunk. The code that returns this error is as follows::

const renderItens = async () => {
   const value = await AsyncStorage.getItem('CART');
   if(value != null){
      let parse = JSON.parse(value)
      return parse.map((item) => {
         return (
            trecho jsx mostrando o meu item.
         )
      })
   }else{
     return (
       trecho jsx falando que ta vazio o carrinho.
     )
   }

}

I can’t do the map because of this error. If I remove the async of the function, the error goes away and my value receives the following reply:: {"_R:0","_N":0,"_W":null, "null":null}, that’s not what I want.

In jsx I call this function as follows:

<ScrollView>{renderItens()}</ScrollView>

What may be happening and how I can solve this problem?

2 answers

2

An asynchronous function returns a Promise, then when using renderItens() you will be rendering this Promise (Object) and not its result (JSX), since it has not yet been solved. See What are javascript (promises)? for more details.

See the difference:

const funcaoAssincrona = async () => "Retorno da função assíncrona";
const funcaoSincrona = () => "Retorno da função síncrona";

console.log(`Função assíncrona não resolvida: ${funcaoAssincrona()}`);
console.log(`Função síncrona: ${funcaoSincrona()}`);

Getting the right result with async/await

See that when trying to display the content of the call funcaoAssincrona(), is displayed [object Promise], so React Native launched the error that it is not possible to render an object in a JSX chunk. If you try to display it within a tag <Text>, could, but would be shown the same as in console.log() above.

To get the right result, you would need to make use of the await. The above example would produce the correct effect in this way:

const funcaoAssincrona = async () => "Retorno da função assíncrona";
const funcaoSincrona = () => "Retorno da função síncrona";

const exibeRetornoAssincrono = async () => console.log(`Função assíncrona resolvida: ${await funcaoAssincrona()}`);

const exibeRetornoSincrono = () => console.log(`Função síncrona: ${funcaoSincrona()}`);

exibeRetornoAssincrono();
exibeRetornoSincrono()

Note that I needed to make use of the await to display the result correctly. Read How ES7 async/await works? to better understand about it.

Rendering an asynchronous content

When there is something asynchronous in a component that will be displayed on the screen it is common to make use of charging states (loading) for the user to understand what is happening, since you need to render something even while it did not obtain the Promise was not resolved.

Then your component could be made as follows (with Hooks, see What is React Hooks?):

function Cart() {
  const [cartItems, setCartItems] = useState([]); // Itens do carrinho
  const [isLoading, setIsLoading] = useState(true); // Começa o componente em estado de carregamento

  useEffect(() => {
    // Quando o componente é montado (`componentDidMount` de um componente Class),
    // carregue o conteúdo do Async Storage

    const loadCartItems = async () => {
      const jsonItems = await AsyncStorage.getItem('CART');
      if (value == null) {
         setCartItems([]); // Nenhum item encontrado
      } else {
         setCartItems(JSON.parse(jsonItems)); // Os itens encontrados
      }
      setIsLoading(false); // Atualiza o estado de "loading" do componente
    }

    loadCartItems();
  }, []);

  // Esse método não é mais assíncrono
  const renderItens = () => {
    if (cartItems.length === 0){
      return <Text>Carrinho vazio</Text>
    } else {
      return cartItems.map(item => <Text>Item: {item}</Text>)
    }
  }

  if (isLoading) {
    return <Text>Carregando...</Text> // Informa o usuário que está carregando
  }

  return <ScrollView>{renderItens()}</ScrollView>
}

If you don’t understand the use of useState or of useEffect, see their documentation.

1


You must do things separately, a function async it takes a while to return, while the component is already rendered. A solution would be:

  1. Have a variable in state, where you will store the items:
    constructor(props) {
        super(props);
        this.state = {
            items: []
        };
    }
  1. Create a function to retrieve data from Storage, calling it when the component is mounted:
    async getData() {
        const value = await AsyncStorage.getItem('CART');
        if (value != null) {
            let parse = JSON.parse(value);
            this.setState({ items: parse });
        }
    }

    componentDidMount() {
        this.getData();
    }
  1. Finally, render the content from your state items:
    render() {
        const { items } = this.state;

        if (items.length === 0) {
            return (
                <View>
                    <Text>trecho jsx falando que ta vazio o carrinho.</Text>
                </View>
            );
        }

        return (
            <View>
                {items.map((item, index) => (
                    <View key={index}>
                        <Text>trecho jsx mostrando o meu item.</Text>
                    </View>
                ))}
            </View>
        );
    }

Browser other questions tagged

You are not signed in. Login or sign up in order to post.