I can't create a new Collection in Firebase. I created a collection called User to store Profildata which works, but now I need another to store something else.
Everything is working but it don't creates the collection on backend.
And I nearly used the same code which I used for the first collection.
function Waffen(){
const [open, setOpen] = React.useState(false);
const handleClose = () => setOpen(false);
const handleOpen = () => setOpen(true);
const [Hersteller, setHersteller] = useState(['Baretta',
'Walther']);
const [Modell, setModell] = useState();
const [Kaliber, setKaliber] = useState();
const history = useHistory("");
const [data] = useState({loading: false,});
const { loading } = data;
const handleSubmit = async (e) => {
e.preventDefault();
await setDoc(doc(db, 'Waffen' ,auth.currentUser.uid),
{ Hersteller: Hersteller, Modell: Modell, Kaliber: Kaliber
});
setOpen(false)
history.replace("/Profil");
};
return (
<div >
<Button onClick={handleOpen}>Meine Waffen</Button>
<Modal
open={open}
onClose={handleClose}>
<Box sx={style} >
<form >
<Grid
container
justifyContent="center"
alignItems="center"
spacing={2}
>
<Autocomplete
disablePortal
id="combo-box-demo"
options={Hersteller}
sx={{ width: 300 }}
renderInput={(params) =>
<TextField {...params} label='Hersteller'
value={Hersteller} onChange={event =>
setHersteller(event.target.value)}
/>}
/>
<Grid item>
<TextField sx={{ width: 300}} variant="outlined"
color="secondary" label="Modell" type="text"
name="Modell" value={Modell} onChange={event =>
setModell(event.target.value)}>
</TextField>
</Grid>
<Grid item >
<TextField sx={{ width: 300}} variant="outlined"
color="secondary" label="Kaliber" type="text"
name="Kaliber" value={Kaliber} onChange={event =>
setKaliber(event.target.value)}>
</TextField>
</Grid>
<Grid item>
<button onSubmit={handleSubmit}
className="AnmeldenButton" >
{loading ? "Bearbeitet ..." : "Speichern"}
</button>
</Grid>
</Grid>
</form>
</Box>
</Modal>
</div>
) ;
}
export default Waffen;
Related
So I've created a get request, which shows all the exercises and the options to edit (which will redirect to another url) and delete. I wanted to use material ui's Dialog, so there will be a confirmation before deleting. The problem is that, say I have 3 exercises and want to delete exercise with index 1, when click the on delete button it appears the last index in the element i.e the last exercise, although I want to delete the second. How can I fix that?
And also can I move Dialog in another file for more clear coding?
Exercises.js
const Exercises = () => {
const { categories, deletedCategories, error, isLoading, exercises, open, handleClickOpen, handleClose, deleteExercise } = useFormExercises();
const [showCategories, setShowCategories] = useState(false);
if (isLoading) {
return <div>Loading...</div>
}
if (error) {
return <div>There was an error: {error}</div>
}
return (
<Grid container className='content' >
<Card className='exerciseCard'>
<h1>Exercises</h1>
<FormControlLabel sx={{ width:'500px'}}
label="Show Categories"
control={
<Checkbox
sx={{ml:2}}
checked={showCategories}
onChange={(event) => setShowCategories(event.target.checked)}
/>
}
/>
<Button sx={{pr:6}} variant='fab' href={('/exercises/add')}>
<Add />
</Button>
<Divider />
<Grid className="exerciseContent" >
{exercises.map(exercise => (
<Card>
<tr key={exercise.id}>
<List>
<ListItem
key={exercise.id}
secondaryAction={
<IconButton onClick={handleClickOpen}>
<Delete />
</IconButton>
}>
<Button
sx={{mr:4}}
href={(`/exercises/edit/${exercise.id}`)} >
<Edit />
</Button>
<ListItemText sx={{justifyContent:'center', width:'400px'}}
primary={exercise.exercise_name}
secondary={showCategories ? exercise["exerciseCategories.category_name"] : null}
/>
</ListItem>
<Dialog
open={open}
onClose={handleClose}
style={{ borderColor: 'red' }}
>
<Box sx={{ borderTop: 3, color: 'red' }}>
<DialogTitle sx={{ color: 'black', backgroundColor: 'gainsboro', pl: 11 }}>
Delete Exercise
</DialogTitle>
<DialogContent>
<DialogContentText color='black' >
<Warning fontSize='large' color='error' id='warning' />
Are you sure you want to delete the exercise: {exercise.exercise_name} ?
</DialogContentText>
</DialogContent>
<DialogActions >
<Button variant='contained' onClick={() => deleteExercise(exercise.id)} autoFocus>
Yes
</Button>
<Button variant='outlined' onClick={handleClose}>No</Button>
</DialogActions>
</Box>
</Dialog>
</List>
</tr>
</Card>
))}
</Grid>
</Card>
</Grid>
)
}
export default Exercises
My delete function
useFormExercises.js
...
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
}
const deleteExercise = async (id) => {
try {
await fetch(`${BASE_URL}/exercises/${id}`, {
method: "DELETE",
}).then(response => {
setExercises(exercises.filter(exercise => exercise.id !== id))
return response.json()
})
} catch (error) {
console.log(error)
}
}
And if another thing seems of, please share your opinion!
Thanks in advance!
You have to Imagine I have a node inside this node are comments(messages) and now I want the user who made the comment to be able to edit or even delete his own comment directly, but only the user who created it.
I honestly don't know if it works that way, but I hope someone can help me with this.
Edit: I add a deleteHandler.
function Messages({ forum }) {
console.log(forum);
const dispatch = useDispatch();
const classes = useStyles();
const [message, setMessage] = useState("");
const messageList = useSelector((state) => state.messageList);
const userLogin = useSelector((state) => state.userLogin);
const { userInfo } = userLogin;
const { messages } = messageList;
const handleClick = async () => {
const finalMessage = `${userInfo.userName}: ${message}`;
await dispatch(createMessageAction(forum._id, finalMessage));
dispatch(listMessage());
setMessage("");
};
const deleteHandler = (id) => {
if (window.confirm("Are you sure? you want to delete")) {
dispatch(deleteMessageAction(id));
}
};
useEffect(() => {
dispatch(listMessage());
}, []);
console.log(messages);
return (
<div>
<div className={classes.messagesOuterContainer}>
<div className={classes.messagesInnerContainer}>
<Typography gutterBottom variant="h6">
Comments
</Typography>
{messages
?.filter((message) => message.forumID === forum._id)
?.map((c) => (
<Typography key={c._id} gutterBottom variant="subtitle1">
<strong>{c.messageText} </strong>
<EditIcon />
<IconButton aria-label="delete">
<DeleteIcon onClick={() => deleteHandler(message._id)}/>
</IconButton>
</Typography>
))}
</div>
{userInfo?.userName && (
<div style={{ width: "70%" }}>
<Typography gutterBottom variant="h6">
Comments
</Typography>
<TextField
fullwidth="false"
rows={4}
variant="outlined"
label="add a Comment"
multiline
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<Button
style={{ marginTop: "10px" }}
fullwidth="false"
disabled={!message}
variant="contained"
color="primary"
onClick={handleClick}
>
Send
</Button>
</div>
)}
</div>
</div>
);
My MessageList state:
>messageList
>messages
>0: {_id:"....", forumID:"..", messageText:"...", user:".."
>1: .....
You must have information about the author of the comment. Assuming each entry in messages has that information, create a conditional that checks to see if the current user's id is the same as the message's author id.
Something along the lines of this.
{messages
?.filter((message) => message.forumID === forum._id)
?.map((c) => (
<Typography key={c._id} gutterBottom variant="subtitle1">
<strong>{c.messageText} </strong>
{message.userId === userLogin.id && ( // modify keys names accordingly
<>
<EditIcon />
<IconButton aria-label="delete">
<DeleteIcon />
</IconButton>
</>
)}
</Typography>
))}
I'm trying to bind to a nested array to text inputs from a form. There are 6 blocks and each block contains 3 values. I have initially populated the array for mapping by using:
new Array(6).fill(['','',''])
and rendering the values using 2 loops. One for the 6 blocks and the other 3 values inside each block. The 3 values are the ones binding to the form. I refer to each of the inputs using the second parameter of the map function which is the index.
Here is the code in its entirety.
import {
Card,
CardContent,
CardHeader,
CardMedia,
TextField,
Grid,
Typography,
Button,
} from "#material-ui/core";
import axios from "axios";
import { useState } from "react";
import { useHistory } from "react-router";
export default function CreateEditQuestion() {
const answerPack = new Array(6).fill(["", "", ""]);
const [question, setQuestion] = useState("");
const [answers, setAnswer] = useState(answerPack);
const [TimeAllowed, setTimeAllowed] = useState(30);
const [Score, setScore] = useState(1);
const [Date, setDate] = useState("");
const history = useHistory();
const handleAnswerSet = (value, answerIndex, answerTextIndex) => {
var updatedAnswer = answers;
updatedAnswer[answerIndex][answerTextIndex] = value;
setAnswer(updatedAnswer);
};
const handleSubmit = () => {
let data = {
question,
answer_set: answers,
time_allowed: TimeAllowed,
score_value: Score,
date: Date,
};
for (const [key, value] of Object.entries({ question, Date })) {
if (value.trim().length == 0) {
alert(`${key} has not been filled in`);
return false;
}
}
axios
.post(
"https://localhost:8000/question",
data
)
.then((resp) => {
alert("Succesfully added Question");
history.push("/question");
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<h1>Create Question</h1>
<Card elevation={1}>
<CardHeader></CardHeader>
<CardContent>
<div>
<TextField
fullWidth
label="Question"
variant="outlined"
onChange={(e) => {
setQuestion(e.target.value);
}}
></TextField>
</div>
<Grid container direction={"row"} spacing={4}>
{answers.map((answerTexts, i) => {
return (
<Grid key={i} item md={4} width={50}>
{answerTexts.map((text, j) => {
return (
<div style={{ width: "70%", marginTop: "30px" }}>
<TextField
fullWidth
label={`Answer ${i + 1} - ${j + 1}`}
onChange={(ev) => {
handleAnswerSet(ev.target.value, i, j);
}}
variant="outlined"
/>
<br />
</div>
);
})}
</Grid>
);
})}
</Grid>
<Grid container direction={"row"} spacing={4}>
<Grid item md={5} width={80}>
<Typography variant="h6" gutterBottom gutterTop>
Extra Options
</Typography>
<TextField
label={"Time Allowed : "}
variant="outlined"
defaultValue={TimeAllowed}
onChange={(ev) => {
setTimeAllowed(ev.target.value);
}}
/>{" "}
<TextField
label="Score"
variant="outlined"
defaultValue={Score}
onChange={(ev) => {
setScore(ev.target.value);
}}
/>
</Grid>
<Grid item md={5} width={100}>
<Typography variant="h6" gutterBottom gutterTop>
Question Date
</Typography>
<TextField
type="date"
onChange={(ev) => {
setDate(ev.target.value);
}}
/>
</Grid>
</Grid>
<div align="center">
<Button
onClick={() => {
handleSubmit();
}}
variant="contained"
color="primary"
>
Submit Question
</Button>
</div>
</CardContent>
</Card>
</>
);
}
Problem:
On each block, changing any value also changes all the other corresponding inputs in other blocks, so if the first input on the first block is changed, then all other first inputs in the other blocks also get changed,e.g(changing answer 1-1 also changes 2-1,3-1,4-1, etc). I could not trace why. Only the corresponding values should be changed
This is the function responsible for setting the values.
const handleAnswerSet = (value, answerIndex, answerTextIndex) => {
var updatedAnswer = answers;
updatedAnswer[answerIndex][answerTextIndex] = value;
setAnswer(updatedAnswer);
};
I am trying to insert a google maps map in a React application. I would rather not use a non-official library (the ones that I have found lack documentation) and I have already managed inserting the map.
My problem is that the map is re-rendered every time the state of the parent component changes; although the values that change are completely irrelevant from what the map needs.
After a bit of research (I am new to React) I came across the React.memo() HOC which is supposed to prevent re-renders of child components when their props are unchanged. For some reason however I cannot get it to work correctly. Event when I insert the map inside a component with no props, any change in the parent state results in a re-render of the map.
Here is the parent component:
const CompanyDepotsPopup = () => {
const classes = useStyles();
const dispatch = useDispatch();
const open = useSelector((state) => selectIsDepotsPopupOpen(state));
const company = useSelector((state) => selectSelectedCompany(state));
const depotsStatus = useSelector((state) => selectDepotsStatus(state));
const {t} = useTranslation();
const [value, setValue] = useState(0);
const [phone, setPhone] = useState("");
const handleChange = (event, newValue) => {
setValue(newValue);
};
const closeModal = () => {
dispatch(toggleDepotsPopup({}));
}
useEffect(() => {
if (company) {
dispatch(depotsListed({companyId: company.id}));
}
}, [company])
if (!company) return <></>;
if (depotsStatus === "loading") {
return <CenteredLoader/>
}
function TabPanel(props) {
const {children, value, index} = props;
return (
<div
hidden={value !== index}
style={{height: "100%"}}
>
{value === index && (
<Box boxShadow={3} mt={1} ml={2} mr={2} height={"100%"}>
{children}
</Box>
)}
</div>
);
}
return (
<Dialog fullWidth={true} open={open}
aria-labelledby="company-dialog-popup">
<DialogTitle >
{company.name}
</DialogTitle>
<DialogContent style={{padding: 0, margin: 0}}>
<Divider/>
<Box mr={0} ml={0} mt={0} p={0} height="95%">
<div >
<AppBar position="static">
<Tabs value={value} onChange={handleChange} aria-label="depots tabs" centered>
<Tab label={t("Company's depots list")}/>
<Tab label={t("Add new depot")}/>
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
<DepotsList/>
</TabPanel>
<TabPanel value={value} index={1}>
<Paper>
<Grid container spacing={2}>
<Grid item xs={12} sm={12} md={12} lg={12}>
<TextField
onChange={(event) => setPhone(event.target.value)}
id="phone"
label={t("Phone")}
type="text"
fullWidth
value={phone}
/>
</Grid>
<Grid item xs={12} sm={12} md={12} lg={12}>
<div style={{height: "250px", display: "flex", "flexDirection": "column"}}>
<MyMap
id="myMap"
/>
</div>
</Grid>
<Grid item xs={12} sm={12} md={12} lg={12} align={"center"}>
<Button variant={"outlined"}>
{t("Save")}
</Button>
</Grid>
</Grid>
</Paper>
</TabPanel>
</div>
</Box>
</DialogContent>
<DialogActions style={{marginTop: "20px"}}>
<Button
variant={"outlined"}
onClick={closeModal}
color="secondary"
>
Done
</Button>
</DialogActions>
</Dialog>
)}
And here is the Map component:
import React, {useEffect} from "react";
const Map = ({id}) => {
const onScriptLoad = () => {
const map = new window.google.maps.Map(
document.getElementById(id),
{
center: {lat: 41.0082, lng: 28.9784},
zoom: 8
}
);
const marker = new window.google.maps.Marker({
position: {lat: 41.0082, lng: 28.9784},
map: map,
title: 'Hello Istanbul!'
});
}
useEffect(() => {
if (!window.google) {
const s = document.createElement("script");
s.type = "text/javascript";
s.src = "https://maps.google.com/maps/api/js?key=''"
const x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
s.addEventListener('load', e => {
onScriptLoad();
})
} else {
onScriptLoad();
}
}, []);
return (
<div style={{width: "100%", height: "100%"}} id={id}/>
);
}
const MyMap = React.memo(Map);
export default MyMap;
Every time setPhone is called when the user types the phone and the state changes, the map is re-rendered. Could someone explain to me why the React.memo does not work and how should I proceed in order to avoid re-rendering the map?
I think my guts feeling is this component
function TabPanel(props) {
const {children, value, index} = props;
return (
<div
hidden={value !== index}
style={{height: "100%"}}
>
{value === index && (
<Box boxShadow={3} mt={1} ml={2} mr={2} height={"100%"}>
{children}
</Box>
)}
</div>
);
}
This is defined inside a component, therefore the instance of this component keeps changing after any state change. In order to prevent it, move it outside of the component, like this
function TabPanel()
function CompanyDepotsPopup()
Instead of
function CompanyDepotsPopup() {
function TabPanel()
}
The reason is also your TabPanel wraps everything else.
I need to disable next button until the form is filled out by the user. Full component is attached in here. I need to disable the next button. This form is build useFrom() in react-hook-from with material UI. This is integrated with API data for the address1, city, and the zip fields. Next button is process to the Payment gateway. So I need to disable the button until the form fields are completed. Only need to validate is all the fields are filled and once completed next is show to click.
const AddressForm = ({ checkoutToken, test }) => {
const [shippingCountries, setShippingCountries] = useState([]);
const [shippingCountry, setShippingCountry] = useState('');
const [shippingSubdivisions, setShippingSubdivisions] = useState([]);
const [shippingSubdivision, setShippingSubdivision] = useState('');
const [shippingOptions, setShippingOptions] = useState([]);
const [shippingOption, setShippingOption] = useState('');
const methods = useForm();
const fetchShippingCountries = async (checkoutTokenId) => {
const { countries } = await commerce.services.localeListShippingCountries(checkoutTokenId);
setShippingCountries(countries);
setShippingCountry(Object.keys(countries)[0]);
};
const fetchSubdivisions = async (countryCode) => {
const { subdivisions } = await commerce.services.localeListSubdivisions(countryCode);
setShippingSubdivisions(subdivisions);
setShippingSubdivision(Object.keys(subdivisions)[0]);
};
const fetchShippingOptions = async (checkoutTokenId, country, stateProvince = null) => {
const options = await commerce.checkout.getShippingOptions(checkoutTokenId, { country, region: stateProvince });
setShippingOptions(options);
setShippingOption(options[0].id);
};
useEffect(() => {
fetchShippingCountries(checkoutToken.id);
}, []);
useEffect(() => {
if (shippingCountry) fetchSubdivisions(shippingCountry);
}, [shippingCountry]);
useEffect(() => {
if (shippingSubdivision) fetchShippingOptions(checkoutToken.id, shippingCountry, shippingSubdivision);
}, [shippingSubdivision]);
return (
<>
<Typography variant="h6" gutterBottom>Shipping address</Typography>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit((data) => test({ ...data, shippingCountry, shippingSubdivision, shippingOption }))}>
<Grid container spacing={3}>
<FormInput required name="firstName" label="First name" />
<FormInput required name="lastName" label="Last name" />
<FormInput required name="address1" label="Address line 1" />
<FormInput required name="email" label="Email" />
<FormInput required name="city" label="City" />
<FormInput required name="zip" label="Zip / Postal code" />
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Country</InputLabel>
<Select value={shippingCountry} fullWidth onChange={(e) => setShippingCountry(e.target.value)}>
{Object.entries(shippingCountries).map(([code, name]) => ({ id: code, label: name })).map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Subdivision</InputLabel>
<Select value={shippingSubdivision} fullWidth onChange={(e) => setShippingSubdivision(e.target.value)}>
{Object.entries(shippingSubdivisions).map(([code, name]) => ({ id: code, label: name })).map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Options</InputLabel>
<Select value={shippingOption} fullWidth onChange={(e) => setShippingOption(e.target.value)}>
{shippingOptions.map((sO) => ({ id: sO.id, label: `${sO.description} - (${sO.price.formatted_with_symbol})` })).map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
</Grid>
<br />
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Button component={Link} variant="contained" to="/cart" color="secondary">Back to Cart</Button>
<Button type="submit" variant="contained" color="primary">Next</Button>
</div>
</form>
</FormProvider>
</>
);
};
You can use a valid function as mentioned before
const isValid = shippingCountries && shippingCountry && shippingSubdivisions && shippingSubdivision && shippingOptions && shippingOption
<Button disabled={!isValid} />
As you use FormProvider and useForm() i think you use react-hook-form? If yes it is even more easy because it has an integrated validator.
For example you could use it like that
const { register, handleSubmit, setValue } = useForm();
....
<form onSubmit={methods.handleSubmit((data) => test({ ...data, shippingCountry, shippingSubdivision, shippingOption }))}>
<FormInput {...register("firstName, {required: true})} />
</form
With setValue you can set the values of the form from external (for example your useEffect function)
setValue([{ firstName: name }, { secondOption: option }]);
reference code:
<Button type="submit" variant="contained" color="primary" disabled={!this.state.shippingCountry || !this.state.shippingSubdivision || ...}>Next
PS: you can have a function which return true or false for the above and then enable...