I'm making a fake toy buy app using react native and expo, I want to add certain details about the toy to the shopping cart page. I think I have set the data properly (I didn't know how to check), but I'm not able to get and show the stored data.
This is my component that sets up the format for any toy product page. This component is implemented into every toy page, so I used this page to set the data like so:
const SlugFormat = ({id, image, name, price, longDesc, seller, type}) => {
// some functions and states
const setData = async (id, name, price, quantity, image) => {
try {
const jsonValue = JSON.stringify(id, name, price, quantity, image)
await AsyncStorage.setItem('ToyProduct', jsonValue)
} catch(e) {
}
console.log('Done')
}
return (
// code that sets up how the page is laid out
)
}
And this is my cart page where I try to get the data, but nothing shows. It currently only shows 'getData' on the page, not any of the values.
const Cart = () => {
const getData = async () => {
return await AsyncStorage.getItem('ToyProduct');
}
return (
<View>
<Text>{getData.name}</Text>
</View>
)
}
What am I doing wrong?
The way your stringify your value is incorrect.
const jsonValue = JSON.stringify(id, name, price, quantity, image)
Try wrapping it into an object like this:
const jsonValue = JSON.stringify({id, name, price, quantity, image})
Related
I'm building a screen to display products available for sale. At the same time, each product would show its favorited status by the logged in user. To do this, each product loads its corresponding favoriteItem data from the backend.
If successful, 2 things happen:
1-the data is added to an array which is a state in a global context
2- the color of the heart is updated based on the state(by finding it)
The problem is the state always starts with the initial value(empty) before subsequent data is added to the array. As a result, the state ends up with only one item.
Below is some code for demo(I've simplified and omitted code for this demo):
// AccountProvider.tsx
const AccountProvider = ({children}: Props)=> {
const [favorites, setFavorites] = useState([])
const loadFavoriteItemForProduct = async (productId: string)=> {
const favoriteItem = await apiManager.getFavoriteItem(productId)
// favorites always resets to its inital value when this function is called
setFavorites([...favorites, favoriteItem])
}
const account: Account = {
loadFavoriteItemForProduct
}
return (
<AccountContext.Provider value={account}>
{children}
</AccountContext.Provider>
);
}
// ProductCard.tsx
// On mount, each productCard loads its favoriteItem data.
const ProductCard = ({product}: ProductCardProps) => {
const userAccount = useContext(AccountContext)
useEffect(()=>{
// loads product's favoritedItem. Favorites ought to contain
// as many favoriteItems in the backend. But, favorites is reinitialized before
// the new item is added. As a result, favorites contain only the favoriteItem of //the last ProductCard. See Screenshot attached
userAccount.loadFavoritedItemForProduct(product.productId)
}, [])
return (
<div>product.title</div>
)
}
// Products.tsx
const Products = () => {
const explore = useContext(ExploreContext)
return (
<div>
{explore.products.map(product => <ProductCard product={product} />)}
</div>
)
}
// index.tsx
...
<AccountProvider>
<ExploreProvider>
<Products />
</ExploreProvider>
</AccountProvider>
...
I'm just trying to use Context to model the userAccount which contains the users favorites. favorites ought to contain all favoriteItem data from the backend. But it only contains that of the last product. See attached screenshot.
Try passing an updater function to setFavorites.
setFavorites((favorites) => [...favorites, favoriteItem]);
React Docs:
https://beta.reactjs.org/apis/react/useState#updating-state-based-on-the-previous-state
I have home component and i passing props into Product card component
{products.map((product: ProductListStoreType) => (
<ProductCard
key={product.slug}
product={product}
category={product.category}
/>
))}
</div>
Category is just string _id from mongo db
and inside the product card
export default function ProductCard({ product, category }: ProductCardProps) {
const getCategory = async () => {
const { data } = await axios.get(`/api/category/${category}`);
return data.name;
};
const { data } = useSWR('/api/category', getCategory);
console.log(data);
The problem is SWR returns the same data for every loop. the id category is unique because it is from the _id mongo DB. but SWR keeps returning the same data which is from the category _id from the first loop
awlays return the first data for every map
The first parameter of useSWR is a unique key identifying the request. Since the key you are passing does not change (it's always '/api/category'), the data is not refreshed.
The key will be as the first argument to your fetcher, see the docs on arguments.
With these two things in mind, you could do something like this:
const getCategory = async (path) => {
const { data } = await axios.get(path);
return data.name;
};
export default function ProductCard({ product, category }: ProductCardProps) {
const { data } = useSWR(`/api/category/${category}`, getCategory);
I am new to the react world. I am trying to write a code in which when an icon is pressed the Image is added into a favourite category and then on the favourite page the image should be displayed. I am having an issue with not knowing how to dynamically get the value out of the image and add them to favourite category without loosing the pervious value. I think the way I wrote states seems messed up. I write in typescript and use material UI
SearchResults
const [images, setImages] = useState([]);
const [favImage, setFavImage] = useState<ImageProps>({
id: '',
farm: 0,
server: '',
secret: '',
title: ''
});
const handleFavImage = ({ farm, server, id, secret, title }: ImageProps): void => {
setFavImage({ id, farm, server, secret, title });
};
ImageModal
const handleFavourite = (): void => {
setIcon(!icon);
if (!icon === true && handleFavImage !== undefined) {
handleFavImage({ farm, server, id, secret, title });
}
};
Use Image Id as reference.
Use redux store / local storage to store the fav image Ids.
Assuming you have all the images here:
const [images, setImages] = useState([]);
On click of the Icon, pass Image Id of the selected Image and iterate through the images array to find the image props.
just to make you understand how to use the component state array do to this operation:
Lets create a state which will store fav image ids
const [favImages, setFavImages] = useState([]);
On Click will look something like this
const handleOnClick = (imageId) => {
var arr = [...favImages]; //copy array by value
arr.push(imageId)
setFavImages(arr) //override state with new values
}
Now the favImages array has the list of fav image ids.
Now, use this favImages in your favourite page component.
let favImageProps = props.images.filter(image => props.favImages.indexOf(image.id) !== -1)
I had the restriction for not using var and let in the code. So I had to change the code.
const [images, setImages] = useState([]);
const [favImage, setFavImage] = useState<ImageProps[]>([]);
const handleFavImage = ({ farm, server, id, secret, title }: ImageProps): void => {
setFavImage([...favImage, { id, farm, server, secret, title }]);
};
I'm trying to map through JSON data from React with this code...
import {useEffect,useState} from 'react'
function Tourapi() {
const[tours,settour]=useState([])
const[loading,setloading]=useState(true)
useEffect(()=>{
fetchtour()
},[])
const fetchtour= async()=> {
const furl='https://www.natours.dev/api/v1/tours'
const res = await fetch(furl)
const data = await res.json()
settour(data)
setloading(false)
}
if(!loading)
{
return (
<div>
{tours.map((tour)=>(
<h3> {tour.name}</h3>
))}
</div>
)
}
else
{
return <h3>LOADING.....</h3>
}
}
export default Tourapi
but it looks like that JSON data contain additional things and the data array so how can I Map through to get the name and description and so on...
the error that I got TypeError: tours.map is not a function
the JSON data begin with
{"status":"success","results":10,"data":{"data":[{"startLocation":{"type":"Point","coordinates":[-80.185942,25.774772],"description":"Miami, USA","address":"301 Biscayne Blvd, Miami, FL 33132, USA"}
what I mean how can I get out of ("status":"success","results":10,"data":) and get the data its self because it's not my API to control with the response
thanks.
This is what you need, I hope it's help
import { useState, useEffect } from "react";
function TourAPI() {
const [tours, settour] = useState([]);
const [loading, setloading] = useState(true);
useEffect(() => {
fetchtour();
}, []);
const fetchtour = async () => {
const furl = "https://www.natours.dev/api/v1/tours";
const res = await fetch(furl);
const data = await res.json();
await settour(data.data.data);
setloading(false);
};
if (!loading) {
return (
<>
<div className="tour-section">
{tours.map((tours, i) => {
return <h3 key={i}>{tours.name}</h3>;
})}
</div>
</>
);
} else {
return <h3>LOADING.....</h3>;
}
}
export default TourAPI;
I'll walk you through what's in the data, if you console.log(data) in your fetchtour, it'll contain an object that contains properties. It's like this
{status: 'success', results: 10, data: {data}}
So, you want to access one more data inside of that data
That'll lead to console.log(data.data) to see what's in there.
And it'll appear like this.
{data: Array(10)}
So, the data key actually contains the data's array inside of it, the data.data contains an array with 10 indexes in it as you can see. You can try in your browser to see that as well, and each index will contain the properties you want in there, which is name, and many more properties that you need to get like location, etc.
So lead us to the last data, if you console.log(data.data.data), it'll access to data that contains the 10 indexes, which it'll be an array [...] so that we can use map.
That's what you need to setState for the tours
And from that, we're good to use map, because we actually are able to map through an array that contains indexes.
And don't forget to set key for each div inside the map or you'll get the warning fire out from the console in your browser.
My English is not too well so my explanation might be a little bit hard but I hope you can understand what's going on.
I have read:
react passing data from parent to child component
and
https://www.freecodecamp.org/news/pass-data-between-components-in-react/
While they outline the desired method. It does not seem to be working for the following example. I must be doing something obvious, but I cannot seem to figure out what I am doing wrong.
Structure
The structure is a wizard style form (4 steps) below which is contained in newrequest.js. Below each step is a list table component with various items associated with each step.
Think of it like a shopping cart, except you can have multiple carts (step 1), you can leave them in the store filled with all sorts of things (step 2-3) for quite some time before buying anything (step 4).
A user many want to create several carts all at the time, and then add/create/edit other items to the carts over the course of several months before finalizing. The carts, and the items in those carts need to stay grouped together, and they need to be found easily enough by the user to keep working on them at some later date.
User Action:
The user goes to step 1, either makes a new cart, or grabs an existing cart to edit and proceeds to step 2. For the edit option, I have been able to pass the id of an existing cart (list-request-saved.js) to (newrequest.js), edit, save etc. I can also tie the items to the cart with a cartid prop when this is happening the first time.
The Problem:
The issue is I cannot seem to figure out how to pass the cartid akarequestID, to step 2 when editing an existing cart akaSavedRequest so the database can fetch the items which belong in that cart. Without the cartid, the database just spits out all items in any cart by anyone. To fix this, I would need to get the cartid to "newrequest.js" from the child component "list-requests-saved.js " in step 1 (This works ok) and then pass the cartid to child component "list-components.js" in step 2 (This does not work ok).
newrequest.js (parent) // Removed the non-impacting sections
//queries
import { listTestRequests } from '../../graphql/queries'
import { listComponents } from '../../graphql/queries'
import { getSavedRequest } from '../../graphql/queries'
import { getComponent } from '../../graphql/queries'
//components
import SavedRequests from "../../pages/list-requests-saved"
import Components from "../../pages/list-components"
const initialTestRequestState = getInitialState(listTestRequests)
const initialComponentState = getInitialState(listComponents)
function NewRequest(props) {
const [testRequest, setTestRequest] = React.useState(initialTestRequestState)
const [component, setComponent] = React.useState(initialComponentState)
const [editRequest, setEditRequest] = React.useState('')
const [editComponent, setEditComponent] = React.useState('')
const requestTableToParent = (requestID) => {
setEditRequest(requestID)
}
const componentTableToParent = (componentID) => {
setEditComponent(componentID)
}
useEffect(() => {
fetchRequest(editRequest)
async function fetchRequest(id) {
if (!id) return
const requestData = await API.graphql({ query: getSavedRequest, variables: { id } })
setTestRequest(requestData.data.getSavedRequest)
}
}, [editRequest])
useEffect(() => {
fetchComponent(editComponent)
async function fetchComponent(id) {
if (!id) return
const componentData = await API.graphql({ query: getComponent, variables: { id } })
setComponent(componentData.data.getComponent)
}
}, [editComponent])
return (
<form
autoComplete="off"
noValidate
{...props}
>
{
activeStep == 0
? <SavedRequests requestTableToParent={requestTableToParent}/>
: null
}
{
activeStep == 1
? <Components componentTableToParent={componentTableToParent} requestID={editRequest}/>
: null
}
</form >
)
}
export default NewRequest
list-requests-saved.js (child-step-1) // Removed the non-impacting sections
function SavedRequests({requestTableToParent}) {
const renderEditButton = (params) => {
return (
<strong>
<Button
variant="text"
color="primary"
size="small"
style={{ marginLeft: 16 }}
onClick={() => requestTableToParent(params.row.id)}
>
Edit
</Button>
</strong>
)
}
export default (SavedRequests)
list-components.js (child-step-2) // Removed the non-impacting sections
import { listComponents } from '../graphql/queries'
import { deleteComponents as deleteComponentsMutation } from '../graphql/mutations'
function Components({componentTableToParent}, {requestID}) {
const [component, setComponent] = useState([])
const classes = useStyles();
console.log("requestID", requestID) //this is undefined
}
export default (Components)
Additional Commentary: editRequest in "newrequest.js" has the correct value which was set by the edit button in "list-requests-saved.js". When I'm trying to catch the value in the child component "list-components.js", requestID is reporting as undefined. What am I doing wrong?