So I have this component. It receives props and in the componentWillReceiveProps I set those props as state to fill in some form details. And it works when I manually type a url like this http://localhost:3000/dashboard/emailpreview/SKXj7t86agAzmRefG
It works great! However if I click on a react-router's Link that points to that url, the componentWillReceiveProps is not triggered at all, thus my form fields are not prefilled. But again if I perform a manual refresh there everything works. Why does this happen? What's the problem? Why componentWillReceiveProps doesn't trigger on Link?
import React, { Component } from 'react'
import { browserHistory } from 'react-router'
import { Editor } from 'react-draft-wysiwyg'
import { convertToRaw } from 'draft-js'
import { createContainer } from 'meteor/react-meteor-data'
import { Emails } from '../../../../../imports/collections/emails/Emails'
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
import draftToHtml from 'draftjs-to-html'
import { stateFromHTML } from 'draft-js-import-html'
import _ from 'lodash'
class EmailEditor extends Component {
constructor (props) {
super(props)
this.state = {
name: '',
to: '',
subject: '',
html: '',
error: ''
}
}
// TODO: If there is id then fill in the information
// TODO: Subscribe and find and fill in with state
componentWillReceiveProps (nextProps) {
console.log('acsdcdsc', nextProps.email.name)
this.setState({name: nextProps.email.name, to: nextProps.email.to, subject: nextProps.email.subject})
}
handleChange (event) {
const changedOne = event.target.name
const newValue = event.target.value
const newState = {}
newState[changedOne] = newValue
this.setState(newState)
}
saveEmail () {
const self = this
console.log('saveEmail')
const { emailId } = this.props.params
console.log(emailId)
console.log('email')
const { name, to, subject, attachments, editorState } = this.state
console.log('editorState', editorState)
if (_.isEmpty(editorState)) {
self.setState({error: 'Please fill requaired fields'})
return
}
const rawContentState = convertToRaw(editorState.getCurrentContent())
const html = draftToHtml(rawContentState)
console.log('html', html)
const email = {
emailId,
name,
to,
subject,
html,
attachments // TODO: figure out how to send this
}
if (emailId === 'new') {
Meteor.call('emails.insert', email, (err, emailId) => {
if (err) {
self.setState({error: 'Please fill requaired fields'})
return
}
if (emailId) console.log(emailId)
browserHistory.push(`/dashboard/emailpreview/${emailId}`)
})
} else {
Meteor.call('emails.update', email, (err, emailId) => {
if (err) console.log(err)
if (emailId) console.log(emailId)
browserHistory.push(`/dashboard/emailpreview/${emailId}`)
})
}
}
renderEditor () {
return(
<div className="form-group">
<div><label style={{display: 'block', color: 'red', marginBottom: '10px'}}>*</label><input value={this.state.name} onChange={this.handleChange.bind(this)} type="text" name="name" placeholder="Email Name" /></div>
<div><label style={{display: 'block', color: 'red', marginBottom: '10px'}}>*</label><input value={this.state.to} onChange={this.handleChange.bind(this)} type="text" name="to" placeholder="To" /></div>
<div><label style={{display: 'block', color: 'red', marginBottom: '10px'}}>*</label><input value={this.state.subject} onChange={this.handleChange.bind(this)} type="text" name="subject" placeholder="Subject" /></div>
<div><span style={{display: 'block', color: 'red', margin: '10px'}}>{this.state.error}</span></div>
<Editor
toolbarClassName="wysiwig-toolbar"
wrapperClassName="wysiwig-wrapper"
editorClassName="wysiwig-editor"
onEditorStateChange={(editorState) => {
this.setState({
editorState
})
console.log(editorState)
}}
/>
<button onClick={this.saveEmail.bind(this)} className="btn btn-success">Save</button>
<button className="btn btn-primary">Send</button>
<button className="btn btn-primary">Test</button>
</div>
)
}
render () {
console.log('listItems1010', this.state)
console.log('listItems prop11010', this.props.email)
return (
<div className="EmailEditor">
{this.renderEditor()}
</div>
)
}
}
// https://jpuri.github.io/react-draft-wysiwyg/#/docs?_k=jjqinp
// {/* editorState={editorState}
// toolbarClassName="home-toolbar"
// wrapperClassName="home-wrapper"
// editorClassName="home-editor"
// onEditorStateChange={this.onEditorStateChange}
// uploadCallback={uploadImageCallBack} */}
export default createContainer((props) => {
const {emailId} = props.params
Meteor.subscribe('emails')
return {email: Emails.findOne(emailId)}
}, EmailEditor)
As it turned out componentWillReceiveProps is not triggered during initial rendering. It is only triggered on component update. So to setState on initial render I used componentDidMount for instance like this
componentDidMount () {
if (this.props.email) {
this.setState({
name: this.props.email.name,
to: this.props.email.to,
subject: this.props.email.subject,
html: this.props.email.html,
editorState: EditorState.createWithContent(stateFromHTML(this.props.email.html))
})
}
}
So the final code would look like
import React, { Component } from 'react'
import { browserHistory } from 'react-router'
import { Editor } from 'react-draft-wysiwyg'
import { convertToRaw, EditorState } from 'draft-js'
import { createContainer } from 'meteor/react-meteor-data'
import { Emails } from '../../../../../imports/collections/emails/Emails'
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
import draftToHtml from 'draftjs-to-html'
import { stateFromHTML } from 'draft-js-import-html'
import _ from 'lodash'
class EmailEditor extends Component {
constructor (props) {
super(props)
this.state = {
name: '',
to: '',
subject: '',
html: '',
error: '',
editorState: EditorState.createEmpty()
}
}
componentWillReceiveProps (nextProps, nextContext) {
console.log('componentWillReceiveProps nextProps.email.name', nextProps.email.name)
this.setState({
name: nextProps.email.name,
to: nextProps.email.to,
subject: nextProps.email.subject,
html: nextProps.email.html,
editorState: EditorState.createWithContent(stateFromHTML(nextProps.email.html))
})
}
componentDidMount () {
if (this.props.email) {
this.setState({
name: this.props.email.name,
to: this.props.email.to,
subject: this.props.email.subject,
html: this.props.email.html,
editorState: EditorState.createWithContent(stateFromHTML(this.props.email.html))
})
}
}
handleChange (event) {
const changedOne = event.target.name
const newValue = event.target.value
const newState = {}
newState[changedOne] = newValue
this.setState(newState)
}
saveEmail () {
const self = this
console.log('saveEmail')
const { emailId } = this.props.params
console.log(emailId)
console.log('email')
const { name, to, subject, attachments, editorState } = this.state
console.log('editorState', editorState)
if (_.isEmpty(editorState)) {
self.setState({error: 'Please fill requaired fields'})
return
}
const rawContentState = convertToRaw(editorState.getCurrentContent())
const html = draftToHtml(rawContentState)
console.log('html', html)
const email = {
emailId,
name,
to,
subject,
html,
attachments // TODO: figure out how to send this
}
if (emailId === 'new') {
Meteor.call('emails.insert', email, (err, emailId) => {
if (err) {
self.setState({error: 'Please fill requaired fields'})
return
}
if (emailId) console.log(emailId)
browserHistory.push(`/dashboard/emailpreview/${emailId}`)
})
} else {
Meteor.call('emails.update', email, (err, result) => {
if (err) console.log(err)
if (result) console.log('update result', result)
browserHistory.push(`/dashboard/emailpreview/${emailId}`)
})
}
}
renderEditor () {
return (
<div className="form-group">
<div><label style={{display: 'block', color: 'red', marginBottom: '10px'}}>*</label><input
value={this.state.name}
onChange={this.handleChange.bind(this)}
type="text"
name="name"
placeholder="Email Name" /></div>
<div><label style={{display: 'block', color: 'red', marginBottom: '10px'}}>*</label><input
value={this.state.to}
onChange={this.handleChange.bind(this)}
type="text"
name="to"
placeholder="To" /></div>
<div><label style={{display: 'block', color: 'red', marginBottom: '10px'}}>*</label><input
value={this.state.subject}
onChange={this.handleChange.bind(this)}
type="text"
name="subject"
placeholder="Subject" /></div>
<div><span style={{display: 'block', color: 'red', margin: '10px'}}>{this.state.error}</span></div>
<Editor
editorState={this.state.editorState}
toolbarClassName="wysiwig-toolbar"
wrapperClassName="wysiwig-wrapper"
editorClassName="wysiwig-editor"
onEditorStateChange={(editorState) => {
this.setState({
editorState
})
console.log('editorState', editorState)
console.log('this.state', this.state)
}}
/>
<button onClick={this.saveEmail.bind(this)} className="btn btn-success">Save</button>
<button className="btn btn-primary">Send</button>
<button className="btn btn-primary">Test</button>
</div>
)
}
render () {
console.log('render state', this.state)
console.log('render props email', this.props.email)
return (
<div className="EmailEditor">
{this.renderEditor()}
</div>
)
}
}
// https://jpuri.github.io/react-draft-wysiwyg/#/docs?_k=jjqinp
// {/* editorState={editorState}
// toolbarClassName="home-toolbar"
// wrapperClassName="home-wrapper"
// editorClassName="home-editor"
// onEditorStateChange={this.onEditorStateChange}
// uploadCallback={uploadImageCallBack} */}
export default createContainer((props) => {
const {emailId} = props.params
Meteor.subscribe('emails')
return {email: Emails.findOne(emailId)}
}, EmailEditor)
The problem is componentWillReceiveProps is not called for the initial render. Read the documentation about the life cycle methods for a complete understanding http://reactjs.cn/react/docs/component-specs.html#lifecycle-methods
You can simply set the state in the constructor to fix the problem.
constructor (props) {
super(props)
this.state = {
name: props.name,
to: props.email.to,
subject: props.email.subject,
html: '',
error: ''
}
}
That code may generate some undefined errors though, a more complete solution would avoid the undefined errors with something similar to this:
constructor (props) {
super(props)
const { email, name } = props;
this.state = {
name,
to: email && email.to,
subject: email && email.subject,
html: '',
error: ''
}
}
Related
I want to add a demo button. When I select that button it need to fill the form as the data I have given. I tried to implement a button but it is not worked.
This is my code .jsx file
import React, { Component } from 'react';
import { Card } from 'antd';
import axios from 'axios';
import { notification} from "antd";
export default class AddDepartment extends Component {
constructor(props) {
super(props);
this.state = {
admins:[],
empId:[],
admin_id:'',
dept_id:'',
dept_name:'',
dept_manager_name:''
}
}
componentDidMount(){
this.fetchAdmins();
this.fetchAdmins();
}
//Admin data as foreign key
fetchAdmins = () =>{
axios.get("http://127.0.0.1:8000/admin-list/").then(res=>
{
const admins=res.data;
this.setState({admins});
}
)
}
fetchEmployee = () => {
axios.get("http://127.0.0.1:8000/employeeid/").then(res=>
{
const empId=res.data;
this.setState({empId});
}
)
}
formData = (e) => {
this.setState({ [e.target.name]: e.target.value });
this.setState({ radstatus: !this.state.radstatus });
console.log(
this.state.dept_name,
this.state.dept_manager_name
);
console.log(this.state.radstatus);
console.log(this.state.event);
};
drop = (e) =>{
this.setState({[e.target.name]:e.target.value})
console.log(this.state.event)
}
onCreateEmp = () => {
console.log();
}
formSubmit = (e) =>{
e.preventDefault();
const deptdata ={
admin_id:this.state.admin_id,
dept_id:this.state.dept_id,
dept_name:this.state.dept_name,
dept_manager_name:this.state.dept_manager_name,
}
console.log(deptdata)
const args = {
description:
"Data added successfully",
duration: 0,
};
notification.open(args);
var url = "http://127.0.0.1:8000/department-Create/";
fetch(url,{
method: 'POST',
headers:{
'Content-type':'application/json',
},
body: JSON.stringify(deptdata)
}).then((response) => {
alert(response)
}).catch(function(err){
alert(err)
})
}
render() {
return (
<div>
<div className="row">
<div className="col main">
<h1
style={{
color: "DodgerBlue",
fontSize: '30px',
fontWeight:"bold"}}>
<center>ADD DEPARTMENT</center>
</h1>
<br></br><br></br>
<form onSubmit= {this.formSubmit}
style={{
fontWeight:"bold"}}
>
<Card style={{ width: 1000,}}>
<br></br>Admin ID :<select required onChange={this.formData} id="admin" name="admin_id" style={{border: "3px solid #ccc",float: "right",width: "68%",height:45}}>
{this.state.admins.map((ad) =>{
const adId = ad.id;
const adName = ad.username;
return(
<option value={ad.id}>{ad.username}</option>
);
})}
</select>
<br></br><br></br>
<br></br>Department ID : <input required style={{border: "3px solid #ccc",float: "right",width: "68%",height:30}} type="text" onChange= {this.formData} name="dept_id"></input><br></br><br></br>
Name : <input required style={{border: "3px solid #ccc",float: "right",width: "68%",height:30}} type="text" onChange= {this.formData} name="dept_name" ></input><br></br><br></br>
Manager of Department : <input required style={{border: "3px solid #ccc",float: "right",width: "68%",height:30}} type="text" onChange= {this.formData} name="dept_manager_name" ></input><br></br><br></br>
<br></br><br></br>
</Card>
<div>
<br></br><br></br><br></br>
<input style={{width:"100%", cursor: "pointer",backgroundColor:"DodgerBlue",border:"none",padding:"12px 28px",margin:"2px 1px",borderRadius:"2px",fontWeight:"bold"}} type="submit" value="Add Department"></input>
<button>DEMO</button>
</div>
</form>
</div>
</div>
</div>
)
}
}
Please help me to do this. How can I create a function that will fill the form (onClick={this.functionName) Then I need to add that function in onclick. After that when I select the button it need to fill the form.
These are the details i need to add----
admin_id = CR12A
Dept_id = CR101B
Dept_name = Decoration
Dept_manager_name = John
when I select the DEMO button the form will need to be filled with this data in the relevant input session.
I've got component that captures user information and posts it to firebase, when the submit button is hit. Also within this component is a function that gets all posted data, and returns only the posts made by the currently logged in user:
authListener() {
auth().onAuthStateChanged(user => {
if(user){
this.setState({
userDetails:user
},
() =>
firebase.firestore().collection('gig-listing').onSnapshot(querySnapshot => {
let filteredGigs = querySnapshot.docs.filter(snapshot => {
return snapshot.data().user === this.state.userDetails.uid
})
this.setState({
filterGigs: filteredGigs
})
})
) //end of set state
} else {
this.setState({
userDetails:null
})
console.log('no user signed in')
}
})
}
This function works and displays what it should. However, when the submit function (in the same component) is executed, it should redirect to another page, but instead I get no render, and the error
TypeError: Cannot read property 'uid' of null
When I refresh the page however, it shows the correct page.
What this is basically saying is that, on redirect, this.state.userDetails.uid evaluates to null, even though I'm signed in at the time. Any ideas why this is happening and any potential solutions?
Here's the component in it's entirety:
import React from "react";
import Header from "./Header";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import { withStyles } from '#material-ui/core/styles';
import axios from "axios";
import firebase from 'firebase'
import { auth } from 'firebase/app'
import {Link} from 'react-router-dom'
import UniqueVenueListing from './UniqueVenueListing'
const StyledButton = withStyles({
root: {
background: '#54ADA6',
borderRadius: 3,
border: 0,
color: 'white',
height: 30,
padding: '0 30px',
marginRight: '1px'
},
label: {
textTransform: 'capitalize',
},
})(Button);
class GigRegister extends React.Component {
constructor() {
super();
this.state = {
name: "",
venue: "",
time: "",
date: "",
genre: "",
tickets: "",
price: "",
venueWebsite: "",
bandWebsite:"",
userDetails: {},
filterGigs: [],
isLoggedIn:false,
currentToken:{}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleClick() {
console.log("handle click reached");
auth()
.signOut()
.then(() => {
console.log("Successfully signed out");
})
.catch((err) => {
console.log(err);
});
}
authListener() {
auth().onAuthStateChanged(user => {
if(user){
console.log(`this is the user: ${user.uid}`)
this.setState({
userDetails:user
},
() =>
firebase.firestore().collection('gig-listing').onSnapshot(querySnapshot => {
let filteredGigs = querySnapshot.docs.filter(snapshot => {
return snapshot.data().user === this.state.userDetails.uid
})
this.setState({
filterGigs: filteredGigs
})
})
) //end of set state
} else {
this.setState({
userDetails:null
})
console.log('no user signed in')
}
})
}
componentDidMount() {
this.authListener();
}
handleSubmit(e) {
let user = this.state.userDetails.uid;
const gigData = {
name: this.state.name,
venue: this.state.venue,
time: this.state.time,
date: this.state.date,
genre: this.state.genre,
tickets: this.state.tickets,
price: this.state.price,
venueWebsite: this.state.venueWebsite,
bandWebsite: this.state.bandWebsite,
user: user
};
auth()
.currentUser.getIdToken()
.then(function (token) {
axios(
"https://us-central1-gig-fort.cloudfunctions.net/api/createGigListing",
{
method: "POST",
headers: {
"content-type": "application/json",
Authorization: "Bearer " + token,
},
data: gigData,
}
);
})
.then((res) => {
this.props.history.push("/Homepage");
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<div className="gig-register">
<Header />
<div className="heading-container">
<h1>Venue Dashboard</h1> <br></br>
{this.state.userDetails ? (
<h3>You are signed in as {this.state.userDetails.email}</h3>
) : null}
<div className="gig-reg-buttons">
{this.state.userDetails ? (
<StyledButton onClick={this.handleClick}>Sign out </StyledButton>
) : (
<Link to="/" style={{ textDecoration: "none" }}>
<StyledButton>Sign In</StyledButton>
</Link>
)}
<Link to="/Homepage" style={{ textDecoration: "none" }}>
<StyledButton>Go to gig listings</StyledButton>
</Link>
</div>
</div>
<div className="handle-gigs">
<div className="reg-gig-input">
<form onSubmit={this.handleSubmit}>
<h3>Register a gig</h3>
<br></br>
<TextField
placeholder="Event name"
id="name"
name="name"
onChange={this.handleChange}
/>
<TextField
placeholder="Time"
type="time"
label="Enter start time"
id="time"
name="time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
onChange={this.handleChange}
/>
<TextField
id="date"
label="Select date"
type="date"
InputLabelProps={{
shrink: true,
}}
onChange={(e) => {
this.setState({ date: e.target.value });
}}
/>
<TextField
placeholder="Genre"
id="genre"
name="genre"
onChange={this.handleChange}
/>
<TextField
placeholder="Band website"
id="bandWebsite"
name="bandWebsite"
onChange={this.handleChange}
/>
<TextField
placeholder= "Link to ticketing agent"
id="tickets"
name="tickets"
onChange={this.handleChange}
/>
<TextField
placeholder="Price"
id="price"
name="price"
onChange={this.handleChange}
/>
<TextField
placeholder="Venue website"
id="venueWebsite"
name="venueWebsite"
onChange={this.handleChange}
/>
<Button type="submit">Submit</Button>
</form>
</div>
<div className="manage-gigs">
<h3 className="manage-gig">Manage your gigs</h3>
<br></br>
{this.state.userDetails ? (
<UniqueVenueListing gigList={this.state.filterGigs} />
) : (
<h2>no gigs to show</h2>
)}
</div>
</div>
</div>
);
}
}
export default GigRegister
Since the data is loaded from Firestore asynchronously, there is no guarantee that the user is still signed in by the time your filter method runs.
If you want to continue using the user that was signed in when the onAuthStateChanged triggered, use the user parameter that was passed in, instead of the value from the state:
if(user){
console.log(`this is the user: ${user.uid}`)
this.setState({
userDetails:user
},
() =>
firebase.firestore().collection('gig-listing').onSnapshot(querySnapshot => {
let filteredGigs = querySnapshot.docs.filter(snapshot => {
return snapshot.data().user === user.uid
})
The user variable in here is guaranteed to still have the correct value, as it is defined within the same scope. The state doesn't have such a guarantee as it is globally shared.
Alternatively if you want to use the user from the state, check whether it still has a value once you get the data from the database:
if(user){
console.log(`this is the user: ${user.uid}`)
this.setState({
userDetails:user
},
() =>
firebase.firestore().collection('gig-listing').onSnapshot(querySnapshot => {
if (this.state.userDetails) {
let filteredGigs = querySnapshot.docs.filter(snapshot => {
return snapshot.data().user === this.state.userDetails.uid
})
}
else {
console.log("User no longer signed in when database returned results, skipping filtering...");
}
Within my GigRegister component, I have an auth listener checking for auth status, and within that auth listener, I have a axios GET request fetching data that is then filtered through, and set to state:
authListener() {
auth().onAuthStateChanged((user) => {
if (user) {
this.setState({
userDetails: user,
isLoggedIn: true
});
axios
.get(
"https://us-central1-gig-fort.cloudfunctions.net/api/getGigListings"
)
.then((res) => {
let filteredGigs = res.data.filter((gig) => {
return gig.user === this.state.userDetails.uid;
});
this.setState({
filterGigs: filteredGigs,
});
});
} else {
this.setState({
userDetails: null,
});
console.log("no user signed in");
}
});
}
componentDidMount() {
this.authListener();
}
However, this is causing some issue with how to Gig Regiester page loads - it doesn't load the first time, but it does when the page is refreshed. Here's the initial error message:
Unhandled Rejection (TypeError): Cannot read property 'uid' of null
....and here's the entire component:
import React from "react";
import Header from "./Header";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import axios from "axios";
import * as firebase from 'firebase'
import { auth } from 'firebase/app'
import {Link} from 'react-router-dom'
import UniqueVenueListing from './UniqueVenueListing'
class GigRegister extends React.Component {
constructor() {
super();
this.state = {
name: "",
venue: "",
time: "",
date: "",
genre: "",
tickets: "",
price: "",
userDetails:{},
filterGigs:[]
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this)
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleClick(){
console.log('handle click reached')
auth().signOut().then(() => {
console.log('Successfully signed out')
})
.catch(err => {
console.log(err)
})
}
authListener(){
auth().onAuthStateChanged((user)=>{
if(user){
this.setState({
userDetails: user
})
axios.get("https://us-central1-gig-fort.cloudfunctions.net/api/getGigListings")
.then(res=> {
let filteredGigs = res.data
.filter(gig => {
return gig.user === this.state.userDetails.uid
})
this.setState({
filterGigs: filteredGigs
})
})
} else {
this.setState({
userDetails: null
})
console.log('no user signed in')
}
})
}
componentDidMount(){
this.authListener()
}
handleSubmit(e) {
let user = auth().currentUser.uid
const gigData = {
name: this.state.name,
venue: this.state.venue,
time: this.state.time,
date: this.state.date,
genre: this.state.genre,
tickets: this.state.tickets,
price: this.state.price,
user:user
};
auth().currentUser.getIdToken().then(function(token) {
axios("http://localhost:5000/gig-fort/us-central1/api/createGigListing", {
method: "POST",
headers: {
"content-type": "application/json",
"Authorization": "Bearer "+token,
},
data: gigData,
})
})
.then((res) => {
console.log(res);
this.props.history.push('/Homepage')
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<div className="gig-register">
<Header />
<div className = 'heading-container'>
<h1>Venue Dashboard</h1> <br></br>
{
this.state.userDetails ?
<h3>You are signed in as {this.state.userDetails.email}</h3>
:
null
}
<div className = 'gig-reg-buttons'>
{
this.state.userDetails ?
<Button onClick = {this.handleClick}>Sign out </Button>
:
<Link to = '/' style={{ textDecoration: "none" }}>
<Button>Sign In</Button>
</Link>
}
<Link to="/Homepage" style={{ textDecoration: "none" }}>
<Button>Go to gig listings</Button>
</Link>
</div>
</div>
<div className = 'handle-gigs'>
<div className = 'reg-gig-input'>
<form onSubmit={this.handleSubmit}>
<h3>Register a gig</h3>
<br></br>
<TextField
placeholder="Event name"
defaultValue="Event name"
id="name"
name="name"
onChange={this.handleChange}
/>
<TextField
placeholder="Time"
defaultValue="Time"
type="time"
label="Enter start time"
id="time"
name="time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
onChange={this.handleChange}
/>
<TextField
id="date"
label="Select date"
type="date"
defaultValue="2017-05-24"
InputLabelProps={{
shrink: true,
}}
onChange={(e) => {
this.setState({ date: e.target.value });
}}
/>
<TextField
placeholder="Genre"
defaultValue="Genre"
id="genre"
name="genre"
onChange={this.handleChange}
/>
<TextField
placeholder="Tickets"
defaultValue="Tickets"
id="tickets"
name="tickets"
onChange={this.handleChange}
/>
<TextField
placeholder="Price"
defaultValue="Price"
id="price"
name="price"
onChange={this.handleChange}
/>
<Button type="submit">Submit</Button>
</form>
</div>
<div className = 'manage-gigs'>
<h3 className = 'manage-gig'>Manage your gigs</h3>
<br></br>
{ this.state.userDetails ?
<UniqueVenueListing gigList = {this.state.filterGigs}/>
:
<h2>no gigs to show</h2>
}
</div>
</div>
</div>
);
}
}
export default GigRegister
Can anyone suggest a solution so that the GigRegister component loads properly on the first time? I should mention that GigRegister is directed to from the login component, upon successful login.
You are trying to do multiple dependent state changes in one batch
This part needs to be split in 2
authListener(){
auth().onAuthStateChanged((user)=>{
if(user){
this.setState({
userDetails: user
})
axios.get("https://us-central1-gig-fort.cloudfunctions.net/api/getGigListings")
.then(res=> {
let filteredGigs = res.data
.filter(gig => {
return gig.user === this.state.userDetails.uid
})
this.setState({
filterGigs: filteredGigs
})
})
} else {
this.setState({
userDetails: null
})
console.log('no user signed in')
}
})
}
Since you set userDetails and then try to use it in one "batch". Put the data request in a componentDidUpdate or useEffect. I recommend only using functional components with hooks.
React.useEffect(() => {
if (state.userDetails) {
/* data request */
}
}, [userDetails])
But since you use a class you need component did update.
componentDidUpdate() {
if (this.state.userDetails && !this.state.filterGigs) {
/* data request */
}
}
This way your component responds to getting data when it has rerendered with its new props.
Good day so I have a question about firebase and perhaps my code as well I wrote some code in JSX and React linked to Firebase and the Button that I'm using to delete is not working properly.
I'm using Parent Child props to pass the function into the page that is needed to be deleted but there is no functionality. I need help thanks!
this is the parent where the function is located :
import React from 'react';
import fire from '../config/firebase';
import Modal from 'react-modal';
// import "firebase/database";
// import 'firebase/auth';
import NotesCard from './note-card';
Modal.setAppElement('#root');
export default class Notes extends React.Component {
_isMounted = false;
constructor(props) {
super(props);
this.state = {
notes: [],
showModal: false,
loggedin: false
};
this.handleOpenModal = this.handleOpenModal.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
this.handleAddNote = this.handleAddNote.bind(this);
this.handleRemoveNote = this.handleRemoveNote.bind(this);
}
componentDidMount() {
this._isMounted = true;
fire.auth().onAuthStateChanged((user) => {
if(user){
// call firebase from import fire
// grab userData and push it to the dataArray
fire.database().ref(`users/${user.uid}/notes`).on('value', (res) => {
const userData = res.val()
const dataArray = []
for(let objKey in userData) {
userData[objKey].key = objKey
dataArray.push(userData[objKey])
}
// set in the state
if(this._isMounted){
this.setState({
notes: dataArray,
loggedin: true
})
}
});
}else {
this.setState({loggedin: false})
}
});
};
componentWillUnmount() {
this._isMounted = false;
}
handleAddNote (e) {
e.preventDefault()
const note = {
title: this.noteTitle.value,
text: this.noteText.value
}
// reference where we can push it
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes`);
dbRef.push(note)
this.noteTitle.value = ''
this.noteText.value = ''
this.handleCloseModal()
}
handleRemoveNote(key) {
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes/${key}`);
dbRef.remove();
}
handleOpenModal (e) {
e.preventDefault();
this.setState({
showModal: true
});
}
handleCloseModal () {
this.setState({
showModal: false
});
}
render() {
return (
<div>
<button onClick={this.handleOpenModal}>create Note</button>
<section className='notes'>
{
this.state.notes.map((note, indx) => {
return (
<NotesCard
note={note}
key={`note-${indx}`}
handleRemoveNote={this.handleRemoveNote}
/>
)
}).reverse()
}
</section>
<Modal
isOpen={this.state.showModal}
onRequestClose={this.handleCloseModal}
shouldCloseOnOverlayClick={false}
style={
{
overlay: {
backgroundColor: '#9494b8'
},
content: {
color: '#669999'
}
}
}
>
<form onSubmit={this.handleAddNote}>
<h3>Add New Note</h3>
<label htmlFor='note-title'>Title:</label>
<input type='text' name='note-title' ref={ref => this.noteTitle = ref} />
<label htmlFor='note-text'>Note</label>
<textarea name='note-text' ref={ref => this.noteText = ref} placeholder='type notes here...' />
<input type='submit' onClick={this.handleAddNote} />
<button onClick={this.handleCloseModal}>close</button>
</form>
</Modal>
</div>
)
}
}
and this is where the function is being called :
import React from 'react';
import fire from '../config/firebase';
export default class NotesCard extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: false,
note: {}
}
this.handleEditNote = this.handleEditNote.bind(this);
this.handleSaveNote = this.handleSaveNote.bind(this);
}
handleEditNote() {
this.setState({
editing: true
})
}
handleSaveNote(e) {
e.preventDefault()
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes/${this.props.note.key}`);
dbRef.update({
title: this.noteTitle.value,
text: this.noteText.value
})
this.setState({
editing: false
})
}
render() {
let editingTemp = (
<span>
<h4>{this.props.note.title}</h4>
<p>{this.props.note.text}</p>
</span>
)
if(this.state.editing) {
editingTemp = (
<form onSubmit={this.handleSaveNote}>
<div>
<input
type='text'
defaultValue={this.props.note.title}
name='title'
ref={ref => this.noteTitle = ref}
/>
</div>
<div>
<input
type='text'
defaultValue={this.props.note.text}
name='text'
ref ={ref => this.noteText = ref}
/>
</div>
<input type='submit' value='done editing' />
</form>
)
}
return (
<div>
<button onClick={this.handleEditNote}>edit</button>
<button onClick={this.props.handleRemoveNote(this.state.note.key)}>delete</button>
{editingTemp}
</div>
)
}
}
Thank you in advance for taking a look at this code.
Second iteration answer
Working sandbox
Problem
looking at https://codesandbox.io/s/trusting-knuth-2og8e?file=/src/components/note-card.js:1621-1708
I see that you have this line
<button onClick={()=> this.props.handleRemoveNote(this.state.note.key)}>delete
Yet your state.note declared as an empty map in the constructor:
this.state = {
editing: false,
note: {}
}
But never assigned a value using this.setState in the component
Solution
Change it to:
<button onClick={()=> this.props.handleRemoveNote(**this.props.note.key**)}>delete</button>
First iteration answer
NotesCard's buttons is firing the onClick callback on render instead on click event.
This is because you have executed the function instead of passing a callback to the onClick handler
Change
<button onClick={this.props.handleRemoveNote(this.state.note.key)}>delete</button>
To
<button onClick={()=> this.props.handleRemoveNote(this.state.note.key)}>delete</button>
I have written a crud application and it works great!
I have successfully implemented when people click on ADD NEW button form will be visible
Now i am facing challange to hide form when people click on SAVE button, coz, the SAVE another component.
Here you go for my Form.js File:
import React, { Fragment } from "react"
import { connect } from 'react-redux'
const axios = require('axios');
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
id: this.props.selectedData.id,
name: this.props.selectedData.name,
age: this.props.selectedData.age,
};
this.onHandleChange = this.onHandleChange.bind(this);
this.submit = this.submit.bind(this);
}
submit(event) {
const data = {
name: this.state.name,
age: this.state.age,
email: this.state.email
};
if (this.props.isEdit) {
data.id = this.props.selectedData.id;
axios.put('http://127.0.0.1:8000/api/v1/employee/' + data.id + '/', data)
.then((response) => {
this.props.dispatch({ type: 'EDIT_POST', response });
});
} else {
// generate id here for new emplyoee
axios.post('http://127.0.0.1:8000/api/v1/employee/', data)
.then((response) => {
this.props.dispatch({ type: 'ADD_POST', response });
});
}
}
onHandleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
componentDidUpdate(prevProps) {
if (prevProps.selectedData.age !== this.props.selectedData.age) { //Check on email, because email is unique
this.setState({ name: this.props.selectedData.name, age: this.props.selectedData.age })
}
}
render() {
return (
<form>
<div className="form-group">
<input onChange={(event) => this.onHandleChange(event)} value={this.state.name} name="name" type="text" />
</div>
<div className="form-group">
<input onChange={(event) => this.onHandleChange(event)} value={this.state.age} name="age" type="number" />
</div>
<button onClick={(event) => this.submit(event)} type="button">
{this.props.isEdit ? 'Update' : 'SAVE'}
</button>
</form>
);
}
}
export default connect(null)(Form);
And this is my Home.js file:
import React from "react"
import Table from "../components/table"
import Form from '../components/form'
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedData: {name: '', age: ''},
isEdit: false,
isFormVisible: false,
};
}
selectedData = (item) => {
this.setState({selectedData: item,isEdit:true, isFormVisible: true})
}
render() {
return (
<React.Fragment>
{this.state.isFormVisible && <div>
<Form selectedData={this.state.selectedData} isEdit={this.state.isEdit}/>
</div>}
{!this.state.isFormVisible &&
<button onClick={() => this.setState({isFormVisible: true})}>ADD NEW</button>}
<table>
<Table selectedData={this.selectedData} />
</table>
</React.Fragment>
);
}
}
export default Home;
Everything is working fine, only issue is hiding the form when i click on SAVE button.
Can anyone help to hide the form clicing on SAVE button?
Inside your Home.js, create a function that does the state change for you and pass it down as a prop to the Form Component.
Home.js
changeFormState = () => {
this.setState({ isFormVisible: !isFormVisible });
}
<Form
changeFormState={this.changeFormState}
selectedData={this.state.selectedData}
isEdit={this.state.isEdit}
/>
Form.js
<button
onClick={(event) => { this.props.isEdit ? this.submit(event) :
this.props.changeFormState() }} type="button"
>
{this.props.isEdit ? 'Update' : 'SAVE'}
</button>
JS Update
submit(event) {
const data = {
name: this.state.name,
age: this.state.age,
email: this.state.email
};
if (this.props.isEdit) {
data.id = this.props.selectedData.id;
axios.put('http://127.0.0.1:8000/api/v1/employee/' + data.id + '/', data)
.then((response) => {
// Fire an event
this.props.onSave && this.props.onSave();
this.props.dispatch({ type: 'EDIT_POST', response });
});
} else {
// generate id here for new emplyoee
axios.post('http://127.0.0.1:8000/api/v1/employee/', data)
.then((response) => {
// Fire an event
this.props.onSave && this.props.onSave();
this.props.dispatch({ type: 'ADD_POST', response });
});
}
}
hideForm = () => { this.setState({ isFormVisible: false }); }
JSX Update
<Form
selectedData={this.state.selectedData}
isEdit={this.state.isEdit}
onSave={this.hideForm}
/>