how to make an object with multiple object an array in state? - reactjs

This is my code,
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./App.css";
function App() {
let [albums, setAlbums] = useState([]);
useEffect(() => {
const key = "blablabla to keep secret";
const fetchData = async () => {
const result = await axios(
`http://ws.audioscrobbler.com/2.0/?method=artist.gettopalbums&artist=cher&api_key=${key}&limit=10&format=json`
);
setAlbums(result.data.topalbums);
console.log(albums, "data?");
// const { data } = props.location.state;
};
fetchData();
}, []);
return <div className="App"></div>;
}
export default App;
the data I am fetching is in an object with objects inside, in-state I initialized with an empty array, and then after I fetched the data I use setAlbums. I have two problems, the console.log after I fetch just shows me an empty array, but when I console log in render I do get the data but it is an object and not an array of objects, so I can't even map over it, how do I fix this?

Try to do something like this:
setAlbums(album => [...album, results.data.topalbums])
that way you can push results to your array instead of transforming it into object
also if you wish to see updated album then create something like:
useEffect(() => {
console.log(albums)
},[albums])
as setting state is asynchronous therefore it doesn't happen immediately as Brian mentioned, so this code will launch if albums state changes its value

Related

Pull data from firestore using useEffect works on re-render only

Here is my code:
import React, { useEffect, useState } from 'react';
import { getDocs, collection } from 'firebase/firestore';
import { db } from '../firebase-config';
const Home = () => {
const [postList, setPostList] = useState([]);
const postsCollectionRef = collection(db, "data");
useEffect(() => {
const getPosts = async () => {
const data = await getDocs(postsCollectionRef);
let postListArray = []
data.forEach((doc) => {
const post = { ...doc.data() }
postListArray.push(post)
});
setPostList(postListArray)
};
getPosts();
console.log(postList);
}, []);
return (
<div>test</div>
);
};
export default Home;
On loading, the console.log returned an empty array. The spooky thing is when i changed anything , for example
return (
<div>test_epic</div>
);
The console.log shows that it is an array. Anyone has any idea as to why? Please refer to the screepcap as attached.
the first render on loading
I changed anything and components rerendered
Setting state in react is asynchronous, so the data is loaded and the state is set but the console.log statement is executed before the setting state async operation is complete
To make it a bit more clear this is how it works step by step
Component is rendered and postList is initialized with a value of []
useEffect is triggered
Data is fetched
A call to set a new value of postList is placed using setPostList (key here is a call is placed not that the data is actually updated)
You print console.log with a value from Step 1
The call from Step 4 is complete and now the data is actually updated
Here is an article that explains it with examples
And here is another answer that explains this deeply

After useEffect API call, state set by useState for json data being passed to a component as props returns empty array

I'm still a beginner in React and I'm trying to use useEffect to fetch data from an API and then useState to set the state and then pass that state as props to a child component.
But in my child component, it appears as an empty array each time when I do console.log. I understand that on the first render the state of my initial state is an empty array []. But I've been trying to combat this and send the right JSON data but can't seem to do so.
I am trying to do this as I have multiple child components that I wanna send data to.
Below is a workaround I coded up with some digging around but doesn't work either:
const api = 'url string'
const [races, setRaces] = useState([]);
const [races2, setRaces2] = useState([races]);
useEffect(() => {
fetch(api)
.then((resp) => resp.json())
.then((response) => setRaces(response));
}, []);
useEffect(() => {
if (races.length) setRaces2(races);
}, [races]);
<Child data={races2}
But this does not seem work to work either when I do console.log(props.data) in the child component.
This is how normally one would fetch data and try and send the data but in both cases, it's been the same.
const api = 'url string'
const [races, setRaces] = useState([]);
useEffect(() => {
fetch(api)
.then((resp) => resp.json())
.then((response) => setRaces(response));
}, []);
<Child data={races}
Following is a rough flow diagram explaining what I wanna do:
Thank you for your help in advance.
I made this quick example.
Here is what the code does:
Fetching the Data using UseEffect
Storing into State
Passing the State into Component as Props
Fetching the Props and Displaying the data.
Code for App.js
import "./styles.css";
import ChildComponent from "./ChildComponent";
import { useEffect, useState } from "react";
export default function App() {
const [title, setTitle] = useState(null);
// * Init on Page Load
useEffect(() => {
fetchTitle();
}, []);
const fetchTitle = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts/1"
);
const data = await response.json();
setTitle(data.title); //Setting the response into state
};
return (
<div className="App">
<ChildComponent data={title} />
</div>
);
}
Code for ChildComponent.js
export default function ChildComponent({ data }) {
return <div>{data}</div>;
}
I created this Codesandbox. This might help.
https://codesandbox.io/s/elegant-lumiere-cg66nt
Array and object are referential data types, passing as array dependency will not re-run side effect. useEffect dependencies should be primitive data type (string, number, boolean,undefined or null).
useEffect(() => {
if (races.length) setRaces2(races);
}, [races.length])// Dependencies must be primitive data type not referencial.

Need help mapping in json fetch

Can anyone see where I mess up this fetch?
Error message; TypeError: Cannot read properties of undefined (reading 'map')
This is how the data looks like
Happy for any help!
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
export const PressReleaseList = () => {
const [pressReleases, setPressReleases] = useState([null]);
useEffect(() => {
(async function () {
try {
const res = await fetch(
'https://feed.mfn.se/v1/feed/3XXXXX.json'
);
const json = await res.json();
setPressReleases(json.items);
} catch (e) {
console.error(e);
}
})();
}, []);
return (
<Main>
{pressReleases.items.map((item) => (
<TextCell key={item.news_id}>
<CategoryText>{item.title}</CategoryText>
</TextCell>
))}
;
</Main>
);
You are trying to access twice to items. Try setting setPressReleases only with the json variable or apply the map function to pressReleases directly.
And given the error code in your comment, we can see that you are trying to render a list (item.subjects) in a JSX Element instead of primitives.
Double check your key={item.news_id}. Looks like item is missing an 's,' should be key={items.news_id} based off your screenshot.
Initially when your loop runs (before the Ajax call completes), it's running the .map on an array whose only child is a null (derived from const [pressReleases, setPressReleases] = useState([null]);).
Within your loop, when you're calling item.news_id you're essentially doing null.news_id thus your app crashes.
Change your useState to the following:
const [pressReleases, setPressReleases] = useState([]);
This will ensure you have an empty array, thus you don't ever loop until you have data.

How do I correctly implement setState() within useEffect()?

If this useEffect() runs once (using [] as the second parameter), setTicket(response.data) does not update the value of ticket with data. If I run useEffect() with [ticket] as the parameter, it updates the value of ticket with data, but useEffect becomes an infinite loop.
I need it to run once and update the ticket data. I don't think I understand useEffect() and its second parameter.
What do I do to get the expected result?
import React from "react";
import axios from "axios";
import { useState, useEffect } from "react";
const EditTicket = (props) => {
const [ticket, setTicket] = useState("");
useEffect(() => {
axios
.get("http://localhost:4000/tickets/" + props.match.params.id)
.then((response) => {
setTicket(response.data);
console.log({ ticket });
})
.catch(function (error) {
console.log(error);
});
}, []);
return <div>edit</div>;
};
export default EditTicket;
ticket is a local const. It will never change, and that's not what setTicket is trying to do. The purpose of setTicket is to tell the component to rerender. On that next render, a new local variable will be created, with the new value.
Your code is already written the way it should be written, except that your log statement is not providing you with any useful information. If you want to see that it rerenders with the new value you could move the log statement to the body of the component.
const EditTicket = (props) => {
const [ticket, setTicket] = useState("");
console.log('rendering', ticket);
useEffect(() => {
// same as before

How to access data from a state in functional components in your JSX

I am learning RN and have the following code that fetches a single post from an API, I use useState hook to set my post once it has fully fetched and set it to post. How do I now access the data in this post from my JSX? (eg post.title, post.id) since I can't use Flatlist data prop for my case. Also any corrections on my code implementation is welcome
import React, { useState, useEffect, useCallback } from "react";
import { View, StyleSheet, Text } from "react-native";
import moment from "moment";
const SinglePostScreen = props => {
const [isLoading, setIsLoading] = useState(true);
const [post, setPost] = useState();
const fetchSinglePost = async () => {
let post_id = props.navigation.getParam("post_id");
const response = await fetch(
`https://kriss.io/wp-json/wp/v2/posts?_embed&include=${post_id}`
);
const post = await response.json();
setPost(post);
setIsLoading(false);
};
useEffect(
useCallback(() => {
fetchSinglePost();
}),
[setPost]
);
return (
<View>
<Text>This is a post of id {post.id}</Text>
I want to access the data from here but how do i pass it to here. I cant use the Flatlist
data property
</View>
);
};
const styles = StyleSheet.create({});
export default SinglePostScreen;
Your api returns an array, so you should add logic to read from an array when using your 'post' variable.
Another thing, useEffect expects a variable on the second argument, but it's used to define when it updates (when the variables passed change, it gets executed). If you pass the setPost variable, useEffect will only get called at the first render, and whenever you change this variable (which you cant because it's a function defined with const). If you want for it to be called only once, just pass an empty array.

Resources