Good day, I'm new React and firebase, Today, I using React and Firebase to display, add and delete data. I have some data in the firebase and display it. Now, I want delete some of the data, but I don't know. I create a button delete that whenever the user click it the data will be removed. Please help..
import React , { Component, Fragment } from 'react';
class Form extends Component {
constructor(){
super();
this.state = {
db: [],
name: "",
city: ""
}
this.changHandle = this.changHandle.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.removeData = this.removeData.bind(this)
}
componentDidMount() {
const { firestore } = this.props
firestore.collection('cafes').orderBy('name')
.onSnapshot((db) => {
const data = db.docs.map(datas => datas.data())
this.setState({
db: data
})
})
}
changHandle(event){
const {name, value} = event.target
this.setState({[name]:value})
}
handleSubmit(e){
e.preventDefault();
this.props
.firestore
.collection('cafes')
.add({city: this.state.city, name: this.state.name})
}
removeData(id){
this.props
.firestore
.collection('cafes').doc(id).delete();
}
render(){
return (
<div>
<form onSubmit={this.handleSubmit} autoComplete = "off">
<input
type="text"
name="name"
value={this.state.name}
placeholder="Name"
onChange={this.changHandle}
/><br/>
<input
type="text"
name="city"
value={this.state.city}
placeholder="City"
onChange={this.changHandle}
/><br/>
<button type="submit">Add user</button>
</form>
<p>Name:{this.state.name} {this.state.city}</p>
{this.state.db.map(data =>
<div>
<li key={data.id}>{data.name} {data.city}</li>
<button onClick={() => this.removeData(data.id)}>Delete</button>
</div>)
}
</div>
)
}
}
export default Form
MyApplication
The problem is that you are not setting an id for your document in firestore so it is just assigning a random one. In your handle submit button you need to do something like this:
const id = new Date().getTime()
this.props
.firestore
.collection('cafes').doc(id)
.add({city: this.state.city, name: this.state.name, id: id})
Related
I am trying to implement eye/eyeslash in on my Register form in React.
This is a function that's is responsible for changing visibility type and eye icon changing.
import React, { useState } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
export const usePasswordToggle = () => {
const [visible, setVisibility] = useState();
const Icon = <FontAwesomeIcon icon={visible ? "eye-slash" : "eye"} />;
const InputType = visible ? "text" : "password";
return [InputType, Icon];
};
I am trying to implement it in component responsible for registering.
import React, { Component, createRef } from "react";
import { usePasswordToggle } from "./usePasswordToggle";
class Register1 extends React.Component {
EmailR = createRef();
UsernameR = createRef();
PasswordR = createRef();
PasswordConfirmR = createRef();
constructor(props) {
super();
this.state = {
message: "",
password: "",
confirmPassword: "",
};
}
handleSubmit = (event) => {
// alert(this.PasswordR.current.value);
// alert(this.PasswordConfirmR.current.value);
if (this.PasswordR.current.value !== this.PasswordConfirmR.current.value) {
alert("The passwords doesn't match");
return false; // The form won't submit
} else {
alert("The passwords do match");
return true; // The form will submit
}
};
onCreateAccount = () => {
let loginInfo = {
Username: this.UsernameR.current.value,
Email: this.EmailR.current.value,
Password: this.PasswordR.current.value,
};
fetch("http://localhost:5000/api/authenticate/register", {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify(loginInfo),
})
.then((r) => r.json())
.then((res) => {
if (res) {
this.setState({
message:
"New Account is Created Successfully. Check your email to verify Account.",
});
}
});
};
render() {
return (
<div>
<h2 className="FormDescription">
{" "}
Please enter Account details for registration
</h2>
<div className="Form">
<p>
<label>
Email: <input type="text" ref={this.EmailR} />
</label>
</p>
<p>
<label>
Username: <input type="text" ref={this.UsernameR} />
</label>
</p>
<div>
<label>
Password:{" "}
<input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">
{usePasswordToggle.Icon}
</span>
</div>
<p>
<label>
ReenterPassword:{" "}
<input type="password" ref={this.PasswordConfirmR} />{" "}
</label>
</p>
<button onClick={this.handleSubmit}> Create </button>
<p>{this.state.message}</p>
</div>
</div>
);
}
}
export default Register1;
My password is always visible, and eye icon is even not visible on the form (it should be inside my input field, but it is not).
Focus on this code snippet:
<div>
<label>
Password: <input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">{usePasswordToggle.Icon}</span>
</div>
Any suggestion what is the problem?
Change this
const [visible, setVisibility] = useState();
to this
const [visible, setVisible] = useState(true);
as the official documentation here
First, add a default value to your useState, either true or false depending on which icon you want to render first.
Then, you should add a onClick method to your icon which will toggle the visibility state. You're setting the icon based on visible value, but you never toggle the value.
onClick={() => setVisibility(!visible)}
UPDATE
You also need to execute your Hook inside your main component (because yes, you wrote what React call a Hook), like so :
const [inputType, icon] = usePasswordToggle();
But doing so, you'll get an error from React that say you cannot use a Hook within a class component due to how they work.
Basically you need to change your Register1 component to be a functional component, and not a class anymore. Look here for a quick overview on how to : https://reactjs.org/docs/components-and-props.html
I've trying to:
Upload image to firebase storage
create field 'fileUrl' in Firestore under the 'articles' document
reference the doc in Firestore so that each image knows to which article it is assigned.
I managed to do all of that in a way but the issue is that when I submit the form with a title, content and image, it creates a new document in Firestore with just a the image ('fileUrl'), instead of creating a document that includes the rest of the form's data.
I know that this is probably because in my UploadFile.js I'm creating a new document but how do I join the Field created in UploadFile.js with the fields in AddArticle.js?
I'm also getting ''Unhandled Rejection (TypeError): Cannot read property 'state' of undefined' for this line:
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
UploadFile.js
import React from "react";
import firebase from '../Firebase';
function UploadFile() {
const [fileUrl, setFileUrl] = React.useState(null);
const onFileChange = async (e) => {
const file = e.target.files[0]
const storageRef = firebase.storage().ref()
const fileRef = storageRef.child(file.name)
await fileRef.put(file);
setFileUrl(await fileRef.getDownloadURL().then(fileUrl => {
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
.then(() => {
setFileUrl('')
})
} ));
};
return (
<>
<input type="file" onChange={onFileChange} />
<div>
<img width="100" height="100" src={fileUrl} alt=''/>
</div>
</>
);
}
export default UploadFile;
AddArticle.js
import React, { Component } from 'react';
import firebase from '../Firebase';
import UploadFile from '../components/UploadFile';
class AddArticle extends Component {
constructor() {
super();
this.ref = firebase.firestore().collection('articles');
this.state = {
title: '',
content: '',
fileUrl: ''
};
}
onChange = (e) => {
const state = this.state
state[e.target.name] = e.target.value;
this.setState(state);
}
onSubmit = (e) => {
e.preventDefault();
const { title, content, fileUrl } = this.state;
this.ref.add({
title,
content,
fileUrl
}).then((docRef) => {
this.setState({
title: '',
content: '',
fileUrl: '',
documentId: docRef.id
});
this.props.history.push("/")
})
.catch((error) => {
console.error("Error adding document: ", error);
});
}
render() {
const { title, content, fileUrl } = this.state;
return (
<div className="container">
<br></br><br></br><br></br>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title text-center">
Create a new article
</h3>
</div>
<br></br><br></br>
<div className="panel-body">
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label for="title">Title:</label>
<input type="text" className="form-control" name="title" value={title} onChange={this.onChange} placeholder="Title" />
</div>
<div className="form-group">
<label for="content">Content:</label>
<textArea className="form-control" name="content" onChange={this.onChange} placeholder="Content" cols="80" rows="20">{content}</textArea>
</div>
{/* <input type="file" onChange={this.onFileChange} /> */}
<UploadFile onChange={this.onChange} value={fileUrl}/>
<button type="submit" className="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
export default AddArticle;
Each time you call firebase.firestore().collection('articles').add(...), you add a new document to the database. Since you call that in both AddArticle and in UploadFile, your image URL and the other fields indeed will end up in separate documents.
The way you're handling the file upload is a bit unusual to me, as I'd normally expect that upload to happen when the other fields are also submitted. Right now, it's not clear to me what the image belong to, as J.A.Hernández also commented.
Either way: you'll need to remove one of the firebase.firestore().collection('articles').add(...) calls and instead pass the document ID into that call. Say that you first add the text fields and then upload the image, the UploadFile can then update the document with:
firebase.firestore().collection('articles').doc(documentId).update({
fileUrl: fileUrl
})
One way to pass the document ID from the to is by keeping it in the state:
this.ref.add({
title,
content,
fileUrl
}).then((docRef) => {
this.setState({
title: '',
content: '',
fileUrl: '',
documentId: docRef.id
});
this.props.history.push("/")
})
And then:
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
My first React session data storage, so thanks. I am trying to set up inputing data and then placing it in session storage, so it can be edited, viewed or deleted later. There are 2 pieces of data, "title" and "note" to be inputed into a form. Nothing happens when I type into the form inputs. Any other help welcome also.
class AddNote extends Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
}
}
componentDidMount() {
this.getFormData();
}
//let notes = getSessionItem(keys.notes);
//if (!notes) { notes = ""; }
onTitleChange(event) {
this.setState({ title: event.target.value }, this.storeFormData);
this.storeFormData();
}
onContentChange(event) {
this.setState({ content: event.target.value }, this.storeFormData);
}
storeFormData() {
const form = {
title: this.state.title,
content: this.state.content
}
setSessionItem(keys.user_form, form);
}
getFormData() {
const form = getSessionItem(keys.user_form);
if (form) {
this.setState({
title: form.name,
content: form.content
});
}
}
render() {
return (
<div>
<div>
<h2>ADD NOTE PAGE</h2>
</div>
<form classname="nav1">
<div>
<label><b>Title</b></label>
<input type="text"
value={this.state.title}
onchange={this.onTitleChange.bind(this)}
/>
</div>
<div>
<label><b>Content</b></label>
<input type="text"
value={this.state.content}
onchange={this.onContentChange.bind(this)}
/>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default AddNote;
and the storage file:
export const keys = {
title: 'title',
notes: 'notes'
}
export const getSessionItem = function (key) {
let item = sessionStorage.getItem(key);
item = JSON.parse(item);
return item;
}
export const setSessionItem = function (key, value) {
value = JSON.stringify(value);
sessionStorage.setItem(key, value);
}
export const removeSessionItem = function (key) {
sessionStorage.removeItem(key);
}
No need to have 2 change handler for your input. You can do it using a common change handler.
<form classname="nav1">
<div>
<label><b>Title</b></label>
<input type="text"
value={this.state.title}
name="title" <---- Provide name here
onChange={this.onChange}
/>
</div>
<div>
<label><b>Content</b></label>
<input type="text"
value={this.state.content}
name="content" <---- Provide name here
onChange={this.onChange}
/>
</div>
<button type="submit">Submit</button>
</form>
Your onChange function should be, and use callback in setState to call your storeFormData function.
onChange = (e) => {
this.setState({
[e.target.name] : e.target.value
}, () => this.storeFormData())
}
Note: In React we use camelCase, for example, onchange should be onChange and classname should be className.
Also make sure you bind this to storeFormData and getFormData functions, or you can use arrow function's to automatically bind this.
Demo
I am new to react and I can fetch the result from form input fields. Now I need to update those values and submit to the backend. I am struggling to find a way to pass all the input field values at once.
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
data: this.props.location.data
};
}
render() {
return (
<div>
<h2>Update Your Profile</h2>
{items.map(item => (
<Form key={item.uId} onSubmit={this.handleSubmit}>
<label>User Name</label>
<input type="text" defaultValue={item.userName}></input>
<label>Email address</label>
<input type="email" defaultValue={item.email}></input>
</div>
<button type="submit" >Update</button>
</Form>
))}
</div>
);
}
handleSubmit = (e) => {
e.preventDefault();
axios.put('http://localhost:3000/api/user/' + this.state.data, this.state.items).then(response => {
//
});
};
My API call looks like this:
app.put('/api/user/:userId', (req, res, err) => {
User.update(
{ userName: req.body.userName, email: req.body.email },
{
where: {
userId: req.params.userId
}
}
).then(function (rowsUpdated) {
res.json(rowsUpdated)
}).catch(err);
});
How can I modify this code to set a value for this.state.items with all the updated fields values and submit it?
I'd recommend to create a new component to wrap around the <Form /> and move the submit/change event handling to that component for each item. This would allow you to be able to extract individual email/userName for any given <Form /> to send as a PUT to your API endpoint as well as handle the respective input value changes.
Parent Component:
class Parent extends Component {
constructor() {
super();
this.state = {
name: 'React',
items: [
{ uId: 1, email: 'foo#test.com', userName: 'bar' },
{ uId: 2, email: 'baz#test.com', userName: 'foobar' }
]
};
}
render() {
return (
<div>
{this.state.items.map(item =>
<MyForm key={item.uId} item={item} data={this.props.location.data} />)}
</div>
);
}
}
Child/Form Component:
import React, { Component } from 'react';
class MyForm extends Component {
constructor(props) {
super(props);
this.state = {
email: this.props.item.email,
userName: this.props.item.userName
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// https://reactjs.org/docs/forms.html#handling-multiple-inputs
handleChange(e) {
const { target} = event;
const value = target.type === 'checkbox' ? target.checked : target.value;
const { name } = target;
this.setState({ [name]: value });
}
handleSubmit(e) {
e.preventDefault();
const { email, userName } = this.state;
const body = { email, userName };
const json = JSON.stringify(body);
console.log(json);
// axios.put('http://localhost:3000/api/user/' + this.props.data, json).then(response => {});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>User Name</label>
<input type="text" defaultValue={this.state.userName}></input>
<label>Email address</label>
<input type="email" defaultValue={this.state.email}></input>
<button type="submit" >Update</button>
</form>
);
}
}
export default MyForm;
Here is an example in action.
Hopefully that helps!
I have a Component that is handling a contact forum submission from a user. I want to take the state that the user submits and add it to my props data. Right now everything is working, but the handleSubmit, I am not sure how to take the state and pass it to my this.data.props to update the data to include the new object.
My data is an array of Objects. The state takes user input and updates itself. Next I want to take the state object and add it to my props.data and then display it on the screen.
EDIT: UPDATED WITH LATEST CODE
import React, { Component, PropTypes } from 'react';
const testData = [
{
name: 'Joe',
email: 'joemail'
},
{
name: 'Bill',
email: 'billmail'
},
{
name: 'Dude',
email: 'dudemail'
}
]
class FormContact extends Component {
constructor(props) {
super(props)
this.state = {
formValues: {
name: '',
email: ''
}
}
}
handleChange(event) {
let formValues = this.state.formValues;
let name = event.target.name;
let value = event.target.value;
formValues[name] = value;
this.setState({
formValues
});
}
handleSubmit(event) {
event.preventDefault();
console.log("NEW FORM VALUES " + this.state.formValues.name + " " + this.state.formValues.email);
const {name, email} = this.state.formValues
this.props.addContact({name, email});
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label> Name:
<input type="text" name="name" placeholder="Name" value={this.state.formValues["name"]} onChange={this.handleChange.bind(this)} />
</label><br />
<label> Email:
<input type="text" name="email" placeholder="Email" value={this.state.formValues["email"]} onChange={this.handleChange.bind(this)}/>
</label><br />
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
)
}
}
FormContact.PropTypes = {
data: PropTypes.arrayOf(
PropTypes.object
)
}
FormContact.defaultProps = {
data: testData
}
class Contact extends Component {
constructor(props) {
super(props)
this.state = {
data: testData
}
}
addContact(contact) {
this.setState({data: this.state.data.concat(contact)});
}
render() {
const renObjData = this.props.data.map( (anObjectMapped, index) => {
return (<p key={index}>
Name: {anObjectMapped.name} < br/>
Email: {anObjectMapped.email} <br /></p>
)
});
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact data={this.state.data} addContact={this.addContact.bind(this)} />
{renObjData}
</div>
)
}
}
Contact.PropTypes = {
data: PropTypes.arrayOf(
PropTypes.object
)
}
Contact.defaultProps = {
data: testData
}
export default Contact;
What you are looking at here is having a parent container that passes down data as props to the form component. You already have your Contact component so you can make it hold the data state.
How it would work is you would write a function on the Contact component called addContact and it would take a contact as an argument and then set its own state with the new contact IE concat it to its own data array through setting state.
class Contact extends React.Component {
constructor() {
super();
this.state = {
data: testData
}
}
addContact = (contact) => {
this.setState({data: this.state.data.concat(contact)});
};
render() {
const contacts = _.map(this.state.data, (value, index) => {
return <li key={index + value}> {value.email} {value.name} </li>
})
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact data={this.state.data} addContact={this.addContact} />
<h3> Contacts</h3>
<ul>{contacts} </ul>
</div>
)
}
}
and then in your handleSubmit function all you have to do is add
handleSubmit(event) {
event.preventDefault();
const {name, email} = this.state.formValues
this.props.addContact({name, email})
}
this will push it onto the data array in the parent component and then once the parent component updates it will pass that down as props to the form component.
Here is a code pen showing all that in action. http://codepen.io/finalfreq/pen/VKPXoN
UPDATE: Also in Contacts added how to display data, you can easily replace lodash _.map with this.state.data.map(function(value, index)