user16354787
user16354787

Reputation:

Use data from async function on React State with JSX

I'm kinda new on React and JS, and I'm having a problem using the data is returned from ASYNC on State and then passing it as JSX. Here is my code:

(PS: I'm receiving from database the data and its working correctly when i console log from the inside function, but I can't seems to find a way using it outside)

import {useState, useEffect} from 'react';
import {supabase} from '../db/supabase'
import styled from 'styled-components';
//CSS
const Container = styled.div`
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;`

const RandomQuote = styled.h1`
width: 75%;
font-size: 3.65em;
color: #333;`
export default function App() {
    const [quote, setQuote] = useState();

    useEffect(() => {
        getQuotes().then().catch();
    });

    async function getQuotes() {
        const {data, error} = await supabase
            .from('quotes_en')
            .select('quote')
        const getRandomQuote = await data[Math.floor(Math.random() * data.length)];
        if (error) console.table(error)
        setQuote(getRandomQuote);
    };

    return (
        <Container>
            <RandomQuote>{quote}</RandomQuote>
        </Container>
    );
}

So the random_quote is returning: Object quote: "Our life will pass like the traces of a cloud, it will be scattered like mist that is chased by the rays of the sun. For our time is the passing of a shadow. Our lives will run like sparks through the stubble." __proto__: Object with console.log [Object]

While data is returning: Array(4) 0: {quote: "Sometimes it takes sadness to know happiness, nois…ppreciate silence, and absence to value presence."} 1: {quote: "If there is no enemy within, the enemy outside can make us no harm."} 2: {quote: "Our life will pass like the traces of a cloud, it …r lives will run like sparks through the stubble."} 3: {quote: "Little by little, day by day, what is meant for you will find its way"} length: 4 __proto__: Array(0) [Array with Objects]

So can someone firstly explain the problem so I can learn, then sharing a snippet of the updated code would be even better.

Upvotes: 2

Views: 630

Answers (2)

Daniel Villegas
Daniel Villegas

Reputation: 11

You may have already solved this, but thought I would post for others who may read this in the future. Here is how you would want to use async functions in a useEffect.

export default function App() {
    // set initial state to an empty array. Since arrays are immutable
    const [quotes, setQuotes] = useState([]);
    
    // your function named 'getQuotes' should return presumably all the quotes
    async function getQuotes() {
        const {data, error} = await supabase
        // return some array of data
        return data ? data : error
    };

    useEffect(() => {
        getQuotes()
          // spread the array into the state object. 
          .then(response => setQuotes([...response]))
          .catch(err => err);
    // quotes in dependency array to only run effect when quotes changes 
    }, [quotes]);
   
   // whatever logic you use for getting a single quote from `quotes` 
   const quote = quotes.from(...).select(...)

    return (
        <Container>
            <RandomQuote>{quotes}</RandomQuote>
        </Container>
    );
}

Why we use this way of updating state when using useEffect

Remember arrays are immutable, so this is how would you update the contents of quotes array. If you set to something like setQuotes(response), you will trigger an infinite re-render. See more below.

So, if you do it like this:

   useEffect(() => {
     getQuotes().then(response => setQuotes(response)).catch(err => err)
   }, [quotes])

Like this you are not spreading response into the default initial array, are creating setting quotes to a completely new array, which causes the state object to update, which re-renders the component, which triggers the useEffect again. Now you have an infinite loop.

Upvotes: 0

Anton Neverov
Anton Neverov

Reputation: 311

All what you need - its just setQuote after getting it from server, inside of getQuotes. And use this function in hook useEffect for first render.

Like this:

    const [quote, setQuote] = useState();

    const getQuotes = async() => {
        const {data, error} = await supabase
            .from('quotes_en')
            .select('quote')
        const getRandomQuote = await data[Math.floor(Math.random() * data.length)];
        if (error) console.table(error)
        setQuote(getRandomQuote); //Here quote goes to state
    };

    useEffect(() => {
      getQuotes().then().catch() //nasty hack for IDE
    }, []);

Upvotes: 1

Related Questions