I am a newbie learning React Native.
I have still struggled to show (returning) elements through mapping.
The code I have written is attached at the very bottom.
So, I fetched object data from Firestore and would like to map through it. The first problem was that I couldn't map through an object in js, but I solved it by extracting keys in a list. But the second problem happened, which elements couldn't be used and the screen was blank.
Here is the code.
The two console.log are working correctly and showing a single ride info in the terminal, but I don't know what to write below that.
This is just an example of data of rides shown in the console.
The format of "rides" data is kind of complicated which is separated by date so something like below. I have been thinking of the data format too while fetching the data.
{
dateFormat: [{obj}, {obj}, {obj}],
dateFormat: [{obj}, {obj}, {obj}],
dateFormat: [{obj}, {obj}, {obj}]
},
export const HomeScreen = ({ navigation }) => {
const { fetchRides, rides } =
useFirestoreContext();
useEffect(() => {
fetchRides();
return;
// await currUserExists();
}, []);
return Object.keys(rides).length > 0 ? (
Object.keys(rides).map((key) => {
console.log(key);
rides[key].map((ride, i) => {
console.log({ ride });
return <Text>{ride.boardType}</Text>;
});
})
) : (
<Text>Loading...</Text>
);
};
Related
I'm trying to make a filter based on a Strapi relation.
My Strapi response looks like this:
I'm using Next Js for the frontend, so I assign props like so:
return {
props: {
projects: data.projects.data,
categories: categoriesData.data.categories.data,
},
}
I map through all of the project and list them all as cards. Then I want to filter them based on a button click. The buttons are the categories names and I map over that array as well. On click of each button I run a function called "handleProjects" and this is where I run into an issue.
I have previously made a filter using SanityCMS and Next Js, however, the code structure of the response in Sanity is much clearer compared to that of Strapi.
This is the code I've used in my previous project with Sanity:
const [filteredProducts, setFilteredProducts] = useState(products)
function handleProducts(e) {
let categoryType = e.target.value
setFilteredProducts(
products.filter((product) => product.category == categoryType)
)
}
I'm using this query where I alias the category as such and it is much simpler:
const productQuery = `*[_type == "product"] {
_id,
slug,
name,
image,
"category": category->name
}`
My function for my Strapi project looks like this:
function handleProjects(e) {
let categoryType = e.target.value
setFilteredProjects(
projects.filter((project) =>
project.attributes.categories.data.map((i) =>
i.attributes.title.includes(categoryType)
)
)
)
}
I'm having an array of objects, but at some point I have to map over an array of objects one level deeper and see if the categoryType matches some of the object values two levels deep.
Can anyone show me what I'm doing wrong here?
I currently have some issues trying to add the infinite query feature to a recipes app I'm working on using Edamam API.
All the examples I have looked for (even React Query's documentation) implement the infinite scroll using a page/cursor number system... I understand this is the ideal way, but... Edamam API doesn't work this way with paginated queries.
Instead, the API has the following structure for each recipe query we look for (let's assume we are searching for "chicken", this would be the JSON structure):
from: 1,
to: 20,
count: 10000,
_links: {
next: {
href: "https://api.edamam.com/api/recipes/v2?q=chicken&app_key=APIKEYc&_cont=CHcVQBtNNQphDmgVQntAEX4BYldtBAAGRmxGC2ERYVJ2BwoVX3cVBWQSY1EhBQcGEmNHVmMTYFEgDQQCFTNJBGQUMQZxVhFqX3cWQT1OcV9xBB8VADQWVhFCPwoxXVZEITQeVDcBaR4-SQ%3D%3D&type=public&app_id=APPID"
title: "Next Page"
}
},
hits: [{}] ... (This is where the actual recipes are)
As you can see, there is no numbering system for paginated queries, instead, it's a whole URL and it's giving me a hard time since I'm also new to React Query.
I tried the following, but it just fetches the same data over and over again as I reach the bottom of the page:
const getRecipes = async ({ pageParam }) => {
try {
const path = pageParam
? pageParam
: `https://api.edamam.com/api/recipes/v2?q=${query}&app_id=${process.env.REACT_APP_APP_ID}&app_key=${process.env.REACT_APP_API_KEY}&type=public`;
const response = await axios.get(path);
return response.data;
} catch (error) {
console.log(error);
}
const { ref, inView } = useInView();
useEffect(() => {
inView && fetchNextPage();
}, [inView]);
const {
data,
isFetching,
isFetchingNextPage,
error,
status,
hasNextPage,
fetchNextPage,
} = useInfiniteQuery(
["recipes", query],
({ pageParam = "" }) => getRecipes(pageParam),
{
getNextPageParam: (lastPage) => lastPage._links.next.href,
}
);
Since the next page param is a whole URL, I just say that IF there is a pageParam, then use that URL for the request, if not, then do a normal request using the query value the user is searching for.
Please help!
Since the next page param is a whole URL, I just say that IF there is a pageParam, then use that URL for the request, if not, then do a normal request using the query value the user is searching for.
I'd say that this is the correct approach. The only code issue I can see in your example is that you destruct page param, and then pass the page param string to getRecipes:
({ pageParam = "" }) => getRecipes(pageParam),
but in getRecipes, you expect an object to come in (which you again destructure):
const getRecipes = async ({ pageParam }) => {
You can fix that by either changing the call side, or the function syntax, and then it should work.
is there a way to persist data from useLocation() ?
Basically I'm sending data that is located on a nested collection from firebase
(There's a lot of background on my question/explanation, you can just skip to the important bit)
Usually you have no issues when you only have one collection you can always access the UID pretty much from anywhere by sending it from the App.js for example:
function App() {
const [user, setUser] = useState([]);
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
if (authUser) {
setUser(authUser);
} else {
setUser(false);
}
})
}, [])
return (
< div >
<Router>
<Switch>
<Route path = "/SomePath">
<SomeLocation user={user}/>
</Route>
</Switch>
</Router>
</div >
);
}
export default App;
and since the user have all the data you need to get any other piece of information (usually) you don't need to worry about nested collections however, what if I'm using nested collections ?
if you want to access all the data from a nested collection that's also fine you do not require any other extra information apart from the user
Ej:
useEffect(() => {
const dbRef= db.collection("users").doc(user.uid).collection("someCollection")
dbRef.onSnapshot(snapshot => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setDataSomewhere(tempData);
})
}, [user]);
However how you get the data of the uid of an specific document inside a nested location with just the user ? you can't (as far as I'm aware)
The Important bit without all the background
EJ:
if you have let's say a "parent" and he adds "students" which is my case:
and I wanted to edit this "student" that is in a nested collection, let's make an example on the first one the one named "alfonsa" this is the edit form which is in another "/Path"
The way I'm handling the data of the student is the following, when you check the student and you select the edit icon from the data-table it sends some data through useLocation()
//PATH-A (The one with the data table)
const editStudent= {
pathname: '/EDIT_STUDENT',
data: studentData
}
const editStudents= () => {
if(studentData== 0)
{
window.alert("Select a student")
}
else {
history.push(editStudent);
}
};
///EDIT_STUDENT (Edit form)
let studentData= useLocation();
let data = studentData.data;
console.log(data)
const uid = data[0].uid <- Here I get that student specific uid
This is what the console log returns (All correct data):
However all the data disappears on refresh (which makes sense because is no longer getting the useLocation data from the previous "/path" location) this is the question:
How can I keep/store/maintain that data that comes from the useLocation() on refresh
Any help/tip/example is welcome
Forgot to add this
This is basically what comes up when I refresh
UPDATE So I tried localStorage() as someone mention but now I can't access the data because is coming as a string how can I separate the data ?
This is how I'm storing the data:
useEffect(() => {
const localStorageEstData = window.localStorage.getItem("students");
localStorageEstData && setStudentsData(JSON.parse(localStorageEstData));
}, []);
useEffect(() => {
window.localStorage.setItem("students", JSON.stringify(studentsData));
}, [studentsData]);
This is how I'm getting the data + how it shows in console:
let data = window.localStorage.getItem("estudiantes")
is coming as a whole string how can I separate it ?
Since no one posted an answer I will based on what #Daniel Beck said
Instead of using useLocation() that is to pass data from one /Path to another is better to use localStorage for multiple reasons and the more important one is to persist/keep data on refresh.
if you use useLocation() and you refresh the data will disappear because is no longer parsing data from one /Path to another. However the localStorage will persist on refresh which is why is more useful for this case
simple solution for me was:
useEffect(() => {
const localStorageEstData = window.localStorage.getItem("students");
localStorageEstData && setStudentsData(JSON.parse(localStorageEstData));
}, []);
useEffect(() => {
window.localStorage.setItem("students", JSON.stringify(estudiantesData));
}, [studentsData]);
and then in the other end:
const data = JSON.parse(window.localStorage.getItem("students"))
const uid = data[0].uid
that way I have access to all the information not just the uid
My problem is that item stored in object (in an array of objects inside a state hook) is not being rendered on page, but it gets printed with console.log
I fetched some data from the server and it worked as expected, returning an array of two items, one of which is an object containing blog data(blog heading, creator, etc) an another is an array of 'sections' of that blog. Here is how I did it,
This is the initialization
// Get the blog id
const {blog_id} = useParams();
// Declaring our state hooks
const initial_blog_state = {
blog_id: blog_id,
heading: '',
creator: {},
created: '',
section: [],
}
const [blog_state, updateBlogState] = useState(initial_blog_state);
Here is the fetching of data from the server
useEffect(() => {
// Fetching data
Promise.all([
fetch(`http://127.0.0.1:8000/api/blog/${blog_id}`),
fetch(`http://127.0.0.1:8000/api/section/full/${blog_id}`)
]).then(responses => {
// Get a JSON object from each of the responses
return Promise.all(responses.map(response => {
return response.json()
}))
}).then(function (data) {
// Log the data to the console
console.log(data);
// Update state
updateBlogState({
...blog_state,
heading: data[0].heading,
creator: data[0].creator,
created: data[0].created,
section: data[1]
})
}).catch(function (error) {
// if there's an error, log it
console.log(error);
});
}, []);
I think the way I'm updating the section inside the hook can be a problem(although I'm not sure how), because I saw in a stackoverflow answer that objects must always be initialized (which I'm not doing when declaring an array of 'objects')
And here is the thing that needs to be rendered
return (
<div className="container">
<h1>{blog_state.heading}</h1>
<p className="text-small text-muted">{blog_state.creator.username}</p>
{blog_state.section.map(item => {
{console.log(item.paragraph)}
<p>{item.paragraph}</p>
})}
</div>
)
Here blog_state.heaing and blog_state.creator.username are being rendered as desired and also console.log(item.paragraph) prints the correct paragraph on the console window, but item.paragraph doesn't show anything on the page.
Nothing is being returned from your map.
i.e you need to add a return line before the <p>{item.paragraph}</p>
So:
{blog_state.section.map(item => {
console.log(item.paragraph)
return <p>{item.paragraph}</p>
})}
or as an inline return:
{blog_state.section.map(item => <p>{item.paragraph}</p>)}
hope you're well. I've been working on a company list component. I am having a problem with updating it in real time, because the axios call I'm sending to 'getById' after it renders is returning only a promise and not the actual data that it is supposed to and I don't have any idea as to why. So when I push the so called new company that I've just added into the array, which is in state, it is only pushing a promise into the array and not the actual Company. I don't have any idea why this is. What the code is supposed to be doing, is it is supposed to be putting the new company into the database, returning the success result, and then I'm using the item from that to make a fresh get call to the axios DB which is supposed to be returning the information I just entered so that I can then insert it into the same array in state that is within the list that is rendered in the company list. However, as I mentioned, only the promise is coming up for some reason.
At one point I was able to get this working, but I did that by essentially calling, 'componentDidMount' after the promise was pushed into the call back clause of the setState funciton of the push function - which was essentially causing the entire component to re-render. I'm a fairly new coder, but my understanding is is that that is a fairly unconventional way to code something, and contrary to good coding methodologies. I believe I should be able to push it into state, and then have it change automatically. I have attached the relevant code below for you to examine. If you believe you need more please let me know. If someone could please tell me why I am getting this weird promise instead of the proper response object, so that I can then insert that into state, I would greatly appreciate it. I've attached some images below the code snippets that I hope will be helpful in providing an answer. I have also left brief descriptions of what they are.
:
class Companies extends React.Component {
constructor(props) {
super(props);
this.state = {
Companies: [],
formData: { label: "", value: 0 },
};
}
componentDidMount = () => {
this.getListCompanies();
};
getListCompanies = () => {
getAll().then(this.listOfCompaniesSuccess);
};
listOfCompaniesSuccess = (config) => {
let companyList = config.items;
this.setState((prevState) => {
return {
...prevState,
Companies: companyList,
};
});
};
onCompListError = (errResponse) => {
_logger(errResponse);
};
mapCompanies = (Companies) => (
<CompaniesList Companies={Companies} remove={remove} />
);
handleSubmit = (values) => {
if (values.companyName === "PPP") {
this.toastError();
//THIS IS FOR TESTING.
} else {
add(values)
.getById(values.item) //I HAVE TRIED IN TWO DIFFERENT PLACES TO GET THE NEW COMPANY IN. HERE
.then(this.newCompanyPush)
.then(this.toastSuccess)
.catch(this.toastError);
}
};
//THIS CODE RIGHT HERE IS THE CODE CAUSING THE ISSUE.
newCompanyPush = (response) => {
let newCompany = getById(response.item); // THIS IS THE OTHER PLACE I HAVE TRIED.
this.setState((prevState) => {
let newCompanyList = [...prevState.Companies];
newCompanyList.push(newCompany);
return {
Companies: newCompanyList,
};
});
};
toastSuccess = () => {
toast.success("Success", {
closeOnClick: true,
position: "top-right",
});
};
toastError = (number) => {
toast.error(`Error, index is ${number}`, {
closeOnClick: true,
position: "top-center",
});
};
This is the axios call I am using.
const getById = (id) => {
const config = {
method: "GET",
url: companyUrls + id,
withCredentials: true,
crossdomain: true,
headers: { "Content-Type": "application/json" },
};
return axios(config).then(onGlobalSuccess).catch(onGlobalError);
};
After the promise is pushed into the array, this is what it looks like. Which is I guess good news because something is actually rendering in real time.
This is the 'promise' that is being pushed into the array. Please note, when I make the same call in post-man, I get an entirely different response, see below.
This is the outcome I get in postman, when I test the call.