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 }]);
};
Related
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})
I'm building a crypto app with react and redux-toolkit.
I'm trying to find a way to manage the data from the API. More specifically i want to be able to sort by value, volume, etc. and add an "isFavourite" property for each coin but i think (correct me if i'm wrong) that the only way to do this is by copying the data to another state. What i've tried so far was adding another state where i passed the data like this:
const [list, setList] = useState()
useEffect(() => {
setList(data)
}, [data])
//"const coinData = list?.data?.coins" instead of "const coinData = data?.data?.coins"
but then an error occured because the data on the "list" were "undefined".
The code bellow is the one that is running without any problems. How can i manage the API data? Am i on the right path or is there a more slick way to do what i want? Thank you!
function Main () {
const { data, error, isFetching } = useGetCryptosQuery()
if(isFetching) return 'Loading...'
const globalStats = data?.data?.stats
const coinData = data?.data?.coins
const coinList = coinData.map(coin => {
return (
<Coin
price = {coin.price}
key = {coin.uuid}
id = {coin.uuid}
name = {coin.name}
icon = {coin.iconUrl}
/>)
})
return (
<div>
<h2>Main</h2>
{coinList}
</div>
)
}
export default Main
You are on the right track - I set up something similar and added a check for null trying to map the data, and that avoids the error you probably got.
const coinList = coinData ? coinData.map((coin) => {
///...coin component
}) : <div></div>;
Then, instead of an error for undefined data, it will return an empty div - until the data is there, then it will render ok.
I trying to use react-select's Creatable select component to allow users to add multiple CORS Origins to be registered for my authentication server. I would like to be able to allow users to paste full URLs, and have these URLs be transformed into Origins (format: <protocol>://<origin>[:port]) once they are added to the Creatable select.
As an example, the user could paste http://some-domain.com:1234/management/clients?param1=abc¶m2=123#fragment_stuff into the Creatable select, and this whole URL would automatically be converted/added as just its origin components: http://some-domain.com:1234.
This is a reduced version the component I've wrote (TypeScript):
import CreatableSelect from 'react-select/creatable';
...
type MyOptionType = {
label: string,
value: string,
}
function SomeComponent(props:{}) {
const [options, setOptions] = useState<MyOptionType[]>([]);
const onOptionsChanged = (newOptions: OptionsType<MyOptionType>) => {
// Get only options containing valid URLs, with valid origins
const validUrlsWithOrigins = newOptions.filter(option => {
try {
return !!(new URL(option.value).origin);
} catch (error) {
return false;
}
});
// Transform options (e.g.: "http://some-domain.com:1234/abc?def=ghi#jkl" will become "http://some-domain.com:1234")
const newOptionsOrigins = validUrlsWithOrigins
.map(option => new URL(option.value).origin)
.map(origin => ({label: origin, value: origin}));
setOptions(newOptionsOrigins);
}
return <CreatableSelect isMulti options={options} onChange={onOptionsChanged} />
}
While debugging using React Developer Tools, I can see that the state of my component is being transformed accordingly, having only the origin part of my URLs being kept in the state:
The problem is that the Creatable select component is rendering the full URL instead of only the URL's Origin:
Why isn't the Creatable select in sync with the component's state? Is there a way to solve this, or is it a limitation on react-select?
You need to distinguish two things here - options prop of CreatableSelect holds an array of all the possibilites. But the value of this component is managed by value property.
You can check Multi-select text input example on docs page but basically you'll need to:
keep values and option separetly:
const [options, setOptions] = React.useState<MyOptionType[]>([]);
const [value, setValue] = React.useState<MyOptionType[]>([]);
const createOption = (label: string) => ({
label,
value: label
});
<CreatableSelect
isMulti
options={options}
value={options}
onChange={onOptionsChanged}
/>
and modify your onOptionsChanged function
set value of transformed and validated input
add new options to options state variable (all options, without duplicates)
Here's some example:
// Transform options (e.g.: "http://some-domain.com:1234/abc?def=ghi#jkl" will become "http://some-domain.com:1234")
const newOptionsOrigins = validUrlsWithOrigins
.map((option) => new URL(option.value).origin)
.map((origin) => createOption(origin));
setValue(newOptionsOrigins);
//get all options without duplicates
const allUniqueOptions: object = {};
[...newOptionsOrigins, ...options].forEach((option) => {
allUniqueOptions[option.value] = option.value;
});
setOptions(
Object.keys(allUniqueOptions).map((option) => createOption(option))
);
};
If I have a piece of data and I store it in a state as follows:
interface DataType {[key: string]: string;}
const [data, setData] = React.useState<DataType>({
{
"Widget1": "weather",
"Widget2": "timer",
"Widget3": "clock"
});
And I want a function to update a single widget, what would be the best way to do so. I tried using one function for each widget, but that is obviously not best practice.
const widget1Handler = (widget: string) => {
var updatedData = {
Widget1: widget,
Widget2: data.Widget2,
Widget3: data.Widget3,
Widget4: data.Widget4
};
setData(updatedData);
};
Same for Widget2 and so on. Better would be one function for all of them something like this:
const widgetHandler = (widget: string, widgetNr: number ) => {
const updatedData = ???;
setData(updatedData);
}
How can I target one specific value and update its state?
Thank you for your help.
You can dynamically selected the property name by using computed property names. This will allow you can pass in the widget number to widgetHandler, and update the state.
const widgetHandler = (widget: string, widgetNr: number ) => {
// const someData = ???;
setData({
...data,
['Widget' + widgetNr.toString()]: someData
});
}
You can make use of state updater callback and pass in the widget number to the updater function like widget2 and so on
const widgetHandler = (widget: string, widgetNr: number ) => {
// widgetNr should be of the format Widget1, Widget2 and so on
setData(previousData => ({
...previousData, // spreading all the previous values
[widgetNr]: widget // updating the required value
}));
}
I'm just getting into Firebase, and really digging it so far, but I'm having a bit of an issue using a Cloud Function to remove an item from an array in a collection. In the app I'm using, users have a document in a users collection that contains user-specific data that doesn't fit within the authentication model. The data looks like this:
Screenshot of user collection structure.
(It's a fitness-related app, hence the array being called 'workouts')
The user has the ability to create a workout, and when they do, after it's created, a cloud function watching for new workouts adds the ID of the newly created workout to the user document, appending it to the workouts array using arrayUnion:
exports.addWorkoutToAuthorsListOfWorkouts = functions.firestore
.document('workouts/{workoutId}')
.onCreate((snap, context) => {
const id = context.params.workoutId
const name = snap.data().title
const uid = snap.data().author
const workout_reference = { id: id, name: name, uid: uid }
const workoutsRef = admin.firestore().collection("users").doc(uid)
return workoutsRef.update({
workouts: admin.firestore.FieldValue.arrayUnion(workout_reference)
})
})
This works fine, but I want the user to be able to delete workouts, and I didn't add a string to the workouts array, but instead an object like this:
{ id: "1214", name: "My workout", uid: "123asd" }
Now, for this exact use-case I could delete the array in my ReactJS app and then do an update, but I'm going to be adding the ability for users to "like" the workouts of other users, which will result in a reference to the workout being added to "workouts" in their personal user document. So if I, as a creator of a workout, delete my workout, I need to be able to delete it from the workouts array of any user who has it. Using arrayRemove didn't work because (I assume) I couldn't pass the object to be removed.
What's the best-practices way to do this? I made this attempt:
exports.removeWorkoutFromAuthorsListOfWorkouts = functions.firestore
.document('workouts/{workoutId}')
.onDelete((snap, context) => {
const id = context.params.workoutId
const uid = snap.data().author
const name = snap.data().name
let newWorkouts = new Array()
admin.firestore().collection("users").doc(uid).collection("workouts").get().then((querySnapshot) => {
querySnapshot.forEach((doc: any) => {
if (doc.id !== id) {
newWorkouts.push(doc.data())
}
});
});
const workoutsRef = admin.firestore().collection("users").doc(uid)
return workoutsRef.update({
workouts: newWorkouts
})
})
But Firebase didn't like it at all, and I'm new enough to the platform that I realize this is most likely due to the result of a knowledge gap, rather than any problem with Firestore or Cloud Functions.
Any help you could provide would be very much appreciated. Cheers!
UPDATE: Got it working, with the following code:
exports.removeWorkoutFromSubscribersListOfWorkouts = functions.firestore
.document('workouts/{workoutId}')
.onDelete((snap, context) => {
const workoutId = context.params.workoutId
const subscribers = snap.data().subscribers
let newWorkouts = new Array()
subscribers.forEach(subscriber => {
let userRef = admin.firestore().collection("users").doc(subscriber)
return userRef.get().then(doc => {
return userRef.update({
workouts: doc.data().workouts.filter(workout => workout.id !== workoutId)
})
})
})
.catch(() => {
console.log("error")
})
})