0

So I'm trying to fetch data from my API and I'm able to log it into console, but I can't render it into my app using useState. Tried something like that and it completely ignores my h2.

var id = "61bf209bdb079818ec63d9fd";

const Main = () => {
    const [name, setName] = useState("");
    const [fromApi, setFromApi] = useState([]);
    
    const getApiData = () => {
        fetch('http://localhost:3000/myapi')
            .then(res => {
                return res.json();
            })
            .then(data => {
                console.log(data);
                setFromApi(data);
            })
        
        for (var i = 0; i < fromApi.length; i++) {
            if (fromApi[i]._id === id) {
                setName(fromApi[i].name);
            }
        }
    }

    useEffect(() => {
        getApiData();
    }, [])
    
    return (
        <div>
            {name && <h2 className='urname'>Your name: {name}</h2>}
        </div>
    )
}
1
  • Your for loop should be called inside the then() after the fetch. Currently you run the for loop before the data has loaded. Commented Dec 19, 2021 at 13:46

2 Answers 2

1

This is happening because React does not guarantee that the state changes are applied immediately. This makes reading state right after updating (in your case setFromApi) a potential pitfall and can potentially return the existing value due to async nature.

React Doc: https://reactjs.org/docs/react-component.html?#setstate

In your case you're using data receiving from API and running for loop even before fromAPI state gets updated. Instead put fromAPI in dependency array to useEffect and perform your for loop there.

var id = "61bf209bdb079818ec63d9fd";

const Main = () => {
    const [name, setName] = useState("");
    const [fromApi, setFromApi] = useState([]);
    
    const getApiData = () => {
        fetch('http://localhost:3000/myapi')
            .then(res => {
                return res.json();
            })
            .then(data => {
                console.log(data);
                setFromApi(data);
            })
    }

    useEffect(() => {
        getApiData();
    }, [])

    useEffect(() => {
      if(fromApi?.length){
        for (var i = 0; i < fromApi.length; i++) {
            if (fromApi[i]._id === id) {
                setName(fromApi[i].name);
            }
        }
      }
    }, [fromApi])
    
    return (
        <div>
            {name && <h2 className='urname'>Your name: {name}</h2>}
        </div>
    )
}

Sign up to request clarification or add additional context in comments.

Comments

0

I would consider why you have two states of fromApi and name? You could just setName as soon as the data comes back from the API...something like:

    const [name, setName] = useState("");
    
    const getApiData = () => {
        fetch('http://localhost:3000/myapi')
            .then(res => {
                return res.json();
            })
            .then(data => {
                console.log(data);
                for (var i = 0; i < data.length; i++) {
                    if (data[i]._id === id) {
                        setName(data[i].name);
                        // Stop the for loop once match has been found
                        return;
                    }
                }
            })
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.