So I have an app that saves items in a list to another list that is then passed, using JSON, to the localstorage and then that localstorage information has to be retrieved from another page, the thing is that the information comes like this
{"name":"Red Dress","image":"/static/media/Dress.1c414114.png","Price":120,"id":1}
while I want it to come in a format that is like this
Name: "Red Dress",
Image: "/static/media/Dress.1c414114.png",
Price: 120,
id: 1
And the reason I want it to come like this is so that, later on, I could point to this data as {product.Name} and it would give me "Red Dress", {product.Price} and it would give me 120 etc.
Here's the corresponding code, that is where I set the localstorage and declare the useState that would keep the items and add them to a differnet list that is added to the localStorage:
const addItem=(item)=>
{
setProduct([...product, item])
localStorage.setItem('products', JSON.parse(product))
}
const[product, setProduct] = useState([])
const [item]=useState([
{
name: 'Blue Dress',
image: '/static/media/Dress.1c414114.png',
Price: 200,
id: 0
},
{
name: 'Red Dress',
image: '/static/media/Dress.1c414114.png',
Price: 120,
id: 1
},
])
And this is the corresponding code on the page that retrieves the data
const [product, getProduct]= useState([])
const getProducts=()=>
{
let X = JSON.stringify(localStorage.getItem('products'))
getProduct([...product, X])
}
Since I have forgotten to add my map function that shows the objects in question, here it is:
{product.map(item=>
<div>
<h3 className='productName'>{item.name}</h3>
<div className='productImageContainer'>
<img className='productImage'src={item.image}></img>
</div>
<span className='productPrice'><strong>${item.Price}.00</strong></span>
<Button className='removeProductButton' variant='danger'><MdCancel></MdCancel> Remove</Button>
<br></br>
</div>)}
Related
I want to allow the user to choose a teacher's name from a drop down and insert that teacher's "teacher_name" and "teacher_id" as 2 separate fields in Firestore database.
I have the following input field which creates a drop down by "teacher_name". Now I can either pass "teacher_name" OR "teacher_id" under optionValue to insert that field. Is there a way to insert both "teacher_name" AND "teacher_id" as 2 separate fields?
<ReferenceInput
label="Teacher"
source="teacher_name"
reference="teachers"
>
<AutocompleteInput
optionText="teacher_name"
optionValue="teacher_name"
defaultValue={null}
/>
</ReferenceInput>
My Firestore looks like this:
Collection name : teachers
Document Structure :
{
teacher_id : "XXX",
teacher_name : "XXX",
other_fields : "XXX",
}
As mentioned in the Answer which explains How to have the whole object act as optionValue in a SelctInput in react-admin as :
This is possible using parse and format. Format makes sure the
form options in the html are just the name strings. parse translates
the selected option into the format you db needs.
Example :
const choices = [
{ id: '1', name: 'Programming' },
{ id: '2', name: 'Lifestyle' },
{ id: '3', name: 'Photography' },
];
<SelectInput
source="category"
choices={choices}
format={(c) => c.name}
parse={(name) => choices.find((c) => c.name === name)}
/>
There is another answer which explains how to input and create two (or more than two) fields using React-Admin as:
allows users to select an existing record related
to the current one (e.g. choosing the author for a post). if that you
want to create a new record instead. You can do so via the onCreate
prop, as explained in the doc:
import { AutocompleteInput, Create, SimpleForm, TextInput } from 'react-admin';
const PostCreate = () => {
const categories = [
{ name: 'Tech', id: 'tech' },
{ name: 'Lifestyle', id: 'lifestyle' },
]; return (
<Create>
<SimpleForm>
<TextInput source="title" />
<AutocompleteInput
onCreate={(filter) => {
const newCategoryName = window.prompt('Enter a new category', filter);
const newCategory = { id: categories.length + 1, name: newCategoryName };
categories.push(newCategory);
return newCategory;
}}
source="category"
choices={categories}
/>
</SimpleForm>
</Create>
);
}
For more information, you can refer to the Official documentation which explains about the input components and common input props.
I want to change a state of an array which will have values derived from data.js file as a starting point. Changing a state means running function setAllThingsArray and adding new element which will be set using a function setThingsArray based on previous state (thingsArray). Do I need to map allThingsArray to get updated array with added elements and to list them ?
import React from "react";
import data from "../data";
import Stamp from "../components/Stamp";
export default function AddItem(props) {
const [thingsArray, setThingsArray] = React.useState({
key: 9,
img: "../images/stamp1.jpg",
name: "Stamp 5",
description: "AAAA",
rating: 78.0,
});
function addItem() {
setAllThingsArray((prevAllThingsArray) => ({
arr: [
...prevAllThingsArray.arr,
setThingsArray((prevThingsArray) => ({
...prevThingsArray,
key: prevThingsArray.key + 1,
img: "../images/stamp1.jpg",
name: "Stamp 5",
description: "AAAA",
rating: 78.0,
})),
],
}));
}
const [allThingsArray, setAllThingsArray] = React.useState(data);
const allThingsElements = allThingsArray.map((st) => {
return <Stamp key={st.key} st={st} />;
});
return (
<>
<div className="form">
<input type="text" placeholder="Image" className="form--input" />
<input type="text" placeholder="Stamp title" className="form--input" />
<input type="text" placeholder="Description" className="form--input" />
<input type="text" placeholder="Rating" className="form--input" />
{/* <button className="form--button" onClick={addStampToJson(json_stamp)}>
Add Stamp
</button> */}
<button className="form--button" onClick={addItem}>
Add Stamp
</button>
</div>
<section className="stamps-list">{allThingsElements}</section>
</>
);
}
Is it correct to place setThingsArray function inside setAllThingsArray function?
state setters (setAllThingsArray and setThingsArray) are asynchronous, so you cannot use a state update within another state update. setThingsArray itself is also a function (not a state value), so you cannot set it directly into setAllThingsArray either.
I'd propose that you should add another variable to keep an updated object (not an updated state), and then update states separately with the updated object.
function addItem() {
//add an updated object
const updatedThingsArray = {
...thingsArray, //this is an actual previous state
key: prevThingsArray.key + 1,
img: "../images/stamp1.jpg",
name: "Stamp 5",
description: "AAAA",
rating: 78.0,
}
setAllThingsArray((prevAllThingsArray) => ([
...prevAllThingsArray,
updatedThingsArray, //it's become a new object with your updated data
]));
setThingsArray(updatedThingsArray) //update it back to the `thingsArray` as the new state
}
I'm trying to display different links when user hovers different buttons (in react.js) but it doesn't work. It gives an error like this: undefined is not an object (evaluating 'datas[hovering].content'). Do you know what causes that? Here's my code:
Data I'm using:
const datas = [
{title: "Home",content: [{name: "Home",to: "/"}, {name: "Abstract",to: "/#abstract"}],link: "/"},
{title: "Project",content: [{name: "The Idea",to: "/Project#idea"}, {name: "Previous Similar Projects",to: "/Project#prev"}, {name: "Our Work",to: "/Project#work"}],link: "/Project"},
{title: "Team",content: [{name: "Members",to: "/Team#members"}, {name: "Sponsors",to: "/Team#sponsors"}],link: "/Team"},
{title: "Description",content: null,link: "/description"},
{title: "Human Practices",content: [{name: "Collabrations",to: "/hp#collab"}, {name: "Integrated HP",to: "/hp#integrated"}],link: "/hp"},
{title: "Judging Form",content: null,link: "/judging"},
{title: "Gallery",content: null,link: "/gallery"}
]
My Component:
const SubLink: React.FC<{ name: string, to: string }> = function ({ name, to }) {
return <li>
<a onClick={() => closeNav()} href={to}>{name}</a>
</li>
}
Initial declarations:
const [hovering, setHovering] = useState(0)
const [subs, setSubs] = useState<JSX.Element[]>([])
The useEffect Hook:
useEffect(()=>{
const sublist = []
for (var i=0;i<datas[hovering].content!.length;i++) {
console.log(1)
const data = datas[hovering].content![i]
sublist.push(<SubLink key={i} name={data.name} to={data.to} />)
}
setSubs(sublist)
},[hovering])
Where I return the SubLink elements
<div className={styles.list}>
{(() => {
return (subs !== []) ? subs : <Image src="images/igem.png" alt="iGEM" style={{ height: "10rem" }} />
})}
</div>
```
My guess is that the error is in the code you omitted. Something must presumably call setHovering(). If it sets it to a number larger than the length of the datas array then boom datas[hovering] is undefined
The JSON data is used to create dynamic Input fields for each item in the array. I would like the JSON data to be updated to match the quantity selected but am unsure the best way to go about this?
I plan on using Hooks to initially store the number of items selected then update the JSON file with a button press, although I am very open to the JSON file updating onChange. what is the best practise for this can you dynamically create react hooks?
here is my current code(I want the quantity to update in the JSON file).
JSON:
//Json data for the shopping ingredients
export default [
{
bread: {
Quantity: 0,
},
Name: 'Bread',
Price: "1.10",
},
{
milk: {
Quantity: 0,
},
Name: 'Milk',
Price: "0.50",
},
{
cheese: {
Quantity: 0,
},
Name: 'Cheese',
Price: "0.90",
},
{
soup: {
Quantity: 0,
},
Name: 'Soup',
Price: "0.60",
},
{
butter: {
Quantity: 0,
},
Name: 'Butter',
Price: "1.20",
}
]
React:
import React, { useState, useEffect } from "react";
import Data from '../shoppingData/Ingredients';
const ShoppingPageOne = (props) => {
//element displays
const [pageone_show, setPageone_show] = useState("pageOne");
//updates quatity of ingredients
const [bread_quantity, setBread_quantity] = useState(0);
const [milk_quantity, setMilk_quantity] = useState(0);
const [cheese_quantity, setCheese_quantity] = useState(0);
const [soup_quantity, setSoup_quantity] = useState(0);
const [butter_quantity, setButter_quantity] = useState(0);
useEffect(() => {
//sets info text using Json
if (props.showOne) {
setPageone_show("pageOne");
} else {
setPageone_show("pageOne hide");
}
}, [props.showOne]);
return (
<div className={"Shopping_Content " + pageone_show}>
{Data.map((Ingredients) => {
return <div className="Shopping_input" key={Ingredients.Name}>
<p>{Ingredients.Name} £{Ingredients.Price}</p>
<input onChange={} type="number"></input>
</div>
})}
<div className="Shopping_Buttons">
<p onClick={props.next_ClickHandler}>Buy Now!</p>
</div>
</div>
);
};
export default ShoppingPageOne;
Having input fields generated dynamically from a JSON file is great but using static hooks to update the JSON seems rather silly.
To work with a file system you need to have particular modules. You can do it using NodeJS using 'fs' module https://nodejs.org/dist/latest-v15.x/docs/api/fs.html.
You need a separate endpoint that will be responsible for data updating that will be on the server-side.
I created a component with several div elements.
By adding a ?goto= parameter to the url I want to scroll the the relevant element. I now solved that with const itemsRef = useRef([]);.
My main concern now is if that's the right and performance efficient approach with itemsRef.current[element.id] = el. element.id will be unique for each element.
I also found packages such as: https://github.com/Macil/react-multi-ref
But I don't see the disadvantage of my approach yet.
Here you can find my current solution in action: https://codesandbox.io/s/scrolltoref-w5i7m?file=/src/Element.js
import React, { useRef, useEffect, useState } from "react";
import clsx from "clsx";
const blueprint = [
{
id: "3mD59WO",
name: "AUDITORIUM",
position: 0,
rooms: [
{
id: "zR8Qgpj",
name: "Audimax",
subtitle: null,
details: null,
position: 0,
elements: [
{
id: "1jLv04W",
position: 0,
type: "daily",
element: "listing_large",
properties: {
meetingId: null,
capacity: 6
}
},
{
id: "1jLv12W",
position: 1,
type: "daily",
element: "listing_large",
properties: {
meetingId: null,
capacity: 6
}
}
]
}
]
},
{
id: "4mDd9WO",
name: "FOYER",
position: 1,
rooms: [
{
id: "4R8Qgpj",
name: "Speakers Table",
subtitle: null,
details: null,
position: 0,
elements: [
{
id: "2jLv04W",
position: 0,
type: "daily",
element: "listing_large",
properties: {
meetingId: null,
capacity: 6
}
},
{
id: "2jLv12W",
position: 1,
type: "daily",
element: "listing_large",
properties: {
meetingId: null,
capacity: 6
}
}
]
}
]
}
];
export default function Query() {
const itemsRef = useRef([]);
const [currentRef, setCurrentRef] = useState();
useEffect(() => {
const scrollToRef = ref => {
window.scrollTo(0, ref.offsetTop);
};
const goto = "1jLv12W"; // This will become an URL parameter ?goto=:someID in the final version
const ref = itemsRef.current[goto];
setCurrentRef(ref); // This is needed to change the className to highlight
scrollToRef(ref); // Here I assign the ref and the component should scroll to that ref
}, []);
return (
<div key="element">
{blueprint.map(floor => (
<div key={floor.id} style={{ marginTop: 50 }}>
Floor: {floor.name} <br />
<br />
{floor.rooms.map(room => (
<div key={room.id}>
Room Name: {room.name}
<br />
{room.elements.map(element => (
<div
ref={el => (itemsRef.current[element.id] = el)}
className={clsx({
highlight:
currentRef && currentRef === itemsRef.current[element.id]
})}
key={element.id}
style={{ backgroundColor: "green", marginTop: 100 }}
>
ElementID: {element.id}
<br />
</div>
))}
</div>
))}
</div>
))}
</div>
);
}
That's the right approach, usually, you will see useRef([]) when handling multiple animations in a page, and that's exactly how it's done itemsRef.current[element.id] = el.
My main concern now is if that's the right and performance efficient approach
That's directly related to "Why Premature Optimization Is the Root of All Evil".
Premature optimization is spending a lot of time on something that you may not actually need.
You trying to optimize before you have any performance issues. Focus on delivering the product and clean code, find a time for optimization when you actually measured it.
We also don’t want to waste an enormous amount of time doing performance optimization on things that don’t matter. Many development teams get caught up in focusing on optimizing for performance and scale before they have validated their new product functionality.
useRef is basically the same as doing useState({ current: <value you pass in> });
Given your use case, what you have done is sufficient however I would change use ref to initialise with an object since that is what you are actually using as oppose to an array:
const itemsRef = useRef({});
Your code still works but may potentially give you some unexpected behaviour since assigning properties to an array can be a bit weird and is definitely not what you intend to do anyway.
For example with an array you are actually doing this:
[]["<some id>"] = el; // very weird!
vs an object:
{}["<some id>"] = el