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.
Related
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...");
}
I've got a react component controlling authorized posts to my firestore. Users submit text-based information, and it's sent to firebase using axios as a client. Only problem is that when I try to submit data, I get the following error:
Firebase ID token has expired. Get a fresh ID token from your client app and try again".
I suspect that on the client-side, getIdToken() is sending an expired token and I'm not sure how to fix it. I tried setting it to getIdToken(true) which didn't work either.
The only "solution" so far has been changing my laptop time to match the timezone of the server, which obviously isn't the fix I'm after.
So, I just want logged in users to be able to make a post without hitting this error, would appreciate any solutions/suggestions.
Here's the axios post within the GigRegister component.
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(
"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);
});
}
...and here's the express/firebase functions. Including is the FBauth middleware:
const FBAuth = (req, res, next) => {
let idToken;
if(req.headers.authorization && req.headers.authorization.startsWith('Bearer ')){
idToken = req.headers.authorization.split('Bearer ')[1]
} else {
console.error('No token found')
return res.status(403).json({error: 'Unauthorized'})
}
admin.auth().verifyIdToken(idToken)
.then(decodedToken => {
req.user = decodedToken;
return db.collection('users')
.where('userId', '==',req.user.uid)
.limit(1)
.get()
})
.then(data =>{
req.user.venueName = data.docs[0].data().venueName;
return next();
})
.catch(err => {
console.error('Error while verifying token', err)
return res.status(403).json(err)
})
}
app.post('/createGigListing', FBAuth, (req,res) => {
const newGig = {
venueName: req.user.venueName,
name: req.body.name,
time: req.body.time,
price: req.body.price,
genre: req.body.genre,
tickets: req.body.tickets,
date: req.body.date,
user:req.body.user,
createdAt: new Date().toISOString()
}
db
.collection('gig-listing')
.add(newGig)
.then(doc => {
res.json({message: `document ${doc.id} created successfully`})
})
.catch(err =>{
res.status(500).json({error: 'something went wrong'})
console.error(err)
})
})
In case anyone needs it, here's the entire GigRegister 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: [],
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) {
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(
"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 ? (
<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
I have a student who has an app that will host a photo to cloudinary then store it in his database. Everything worked fine locally but when he tried to host his app on heroku his app couldn't find his cloudinary environment variables. As far as I know he has done everything right and his app can find his other variables just not the cloudinary ones. Has anyone run into a problem similar to this and can give me any direction?
import React, { Component } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import Dropzone from 'react-dropzone';
import ReactQuill from 'react-quill';
import request from 'superagent';
const CLOUDINARY_UPLOAD_PRESET = process.env.CLOUDINARY_UPLOAD_PRESET
const CLOUDINARY_UPLOAD_URL = process.env.CLOUDINARY_UPLOAD_URL
export default class UpdateBook extends Component {
constructor(props){
super(props);
this.state={
id: "",
title: "",
blog_status: "",
content: "",
featured_image_url: "",
formHidden: true,
uploadedFile:'',
uploadedFileCloudinaryUrl: '',
editMode: false
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.editBlog = this.editBlog.bind(this)
this.handleContentChange = this.handleContentChange.bind(this)
this.handleImageDrop = this.handleImageDrop.bind(this)
this.componentConfig=this.componentConfig.bind(this);
this.djsConfig=this.djsConfig.bind(this);
this.deleteImage = this.deleteImage.bind(this);
}
handleChange(event){
this.setState({
[event.target.name]: event.target.value
})
}
handleContentChange(value) {
this.setState({ content: value })
}
handleSubmit(event){
let id = this.state.id
let title = this.state.title;
let blog_status = this.state.blog_status;
let content = this.state.content;
let featured_image_url = this.state.featured_image_url;
fetch(`https://rwtw-backend-tw.herokuapp.com/update_blog/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({title: title, blog_status: blog_status, content: content, featured_image_url: featured_image_url})
}).then(response =>{
response.json()
}).then(responseData =>{
return responseData
}).catch(error => console.log("Fetch error", error))
}
handleImageDrop(files){
this.setState({
uploadedFile: files[0]
})
const currentFile = files[0]
const myFileItemReader = new FileReader()
myFileItemReader.addEventListener("load", () => {
this.setState({
image_src: myFileItemReader.result
})
}, false)
myFileItemReader.readAsDataURL(currentFile)
let upload = request.post(CLOUDINARY_UPLOAD_URL)
.field("upload_preset", CLOUDINARY_UPLOAD_PRESET)
.field("file", this.state.uploadedFile);
upload.end((err, response) => {
if (err){
console.log(err)
} if (response.body.secure_url !== ""){
this.setState({
featured_image_url: response.body.secure_url
})
}
})
}
editBlog(){
this.setState({editMode: true})
this.setState({id: this.props.ourProp[0]})
this.setState({title: this.props.ourProp[1]})
this.setState({blog_status: this.props.ourProp[2]})
this.setState({content: this.props.ourProp[3]})
this.setState({featured_image_url: this.props.ourProp[4]})
}
componentConfig(){
return{
iconFileTypes: [".jpg", ".png"],
showFiletypeIcon: true,
postUrl: 'https://httpbin.org/post'
}
}
djsConfig(){
return{
addRemoveLinks: true,
maxFiles: 1
}
}
deleteImage(){
this.setState({
featured_image_url: ""
})
}
render(){
return (
<div>
{this.state.editMode ?
<form>
<div className="blog-manager-title">
<input
type="text"
name="title"
placeholder="Blog Title"
value = {this.state.title}
onChange = {this.handleChange}
/>
</div>
<div className="blog-manager-blog_status">
<select
type="text"
name="blog_status"
value = {this.state.blog_status}
onChange = {this.handleChange}
>
<option value="draft">Draft</option>
<option value="published">Published</option>
</select>
</div>
<div className="blog-manager-content">
<ReactQuill
value={this.state.content}
onChange={this.handleContentChange}
/>
</div>
<div>
{this.state.featured_image_url !== "" ?
<div>
<img src={this.state.featured_image_url} style={{width: '400px'}}/>
<div className="image-removal-link">
<a onClick={() => this.deleteImage()}>Remove Image</a>
</div>
</div > :
<Dropzone
onDrop={this.handleImageDrop}
multiple= {false}
accept="image/*"
>
Add a picture
</Dropzone>
}
</div>
<div className="submit">
<button onClick={this.handleSubmit} value="submit">Add Blog</button>
</div>
</form> : null}
<button onClick={this.editBlog}><FontAwesomeIcon icon="edit"/></button>
</div>
);
}
}
In Heroku, there is a Cloudinary add-on. This will create a new Cloudinary account. So if your student create Cloudinary account outside Heroku, this will be 2 different account.
To double check this, from their Heroku account, try to login to Cloudinary, check if it's using the same cloud name.
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}
/>
I'm trying to create a change password page in react and i'm getting typeError: cannot read property 'users' of null. The code works for other form pages(where i'm doing PUT and CREATE) but not this one
I tried binding the submit handler to the this keyword but that didn't work.
Also tried binding the handlePasswordChange to the this keyword
./formchange
import React from "react";
import { Link } from "react-router-dom";
var createReactClass = require("create-react-class");
var FormChange = createReactClass({
//setting initial state
getInitialState() {
return {
password: {}
};
},
handlePasswordChange(e) {
this.setState({
password: e.target.value
});
},
handleSubmit(e) {
e.preventDefault();
this.props.onSubmit(this.state);
this.props.history.push("/");
},
render() {
return (
<form
name="categories_post"
className="form-horizontal"
onSubmit={this.handleSubmit}
>
<div id="change_password">
<div className="form-group">
<label
className="col-sm-2 control-label required"
htmlFor="password"
>
Password
</label>
<div className="col-sm-10">
<input
type="text"
value={this.state.password}
onChange={this.handlePasswordChange}
id="password"
className="form-control"
/>
</div>
</div>
<button
type="submit"
id="formChangeSubmit"
className="btn btn-default"
>
Submit
</button>
</div>
</form>
);
}
});
export default FormChange;
./passwordupdate
import React from "react";
import { updateUsers, fetchUsers } from "./actions/appactions";
import FormChange from "./formchange";
var createReactClass = require("create-react-class");
const Update = createReactClass({
getIntitialState() {
return {
users: {}
};
},
componentWillReceiveProps(props) {
this.setState(props);
},
componentDidMount() {
fetchUsers(this.props.match.params.usersId)
.then(data => {
this.setState(state => {
state.users = data;
return state;
});
})
.catch(err => {
console.error("error", err);
});
},
handleSubmit(data) {
updateUsers(this.state.users.id, data);
},
render() {
return (
<div>
<FormChange
onSubmit={this.handleSubmit.bind}
password={this.state.users.password}
/>
</div>
);
}
});
export default Update;
//fetchusers function
export function fetchUsers(id) {
return fetch("https://localhost:44341/api/users/" + id, {
method: "GET",
mode: "cors"
})
.then(res => res.json())
.catch(err => err);
}
<FormChange
onSubmit={this.handleSubmit.bind(this)}
password={this.state.users.password}
/>
make this change and check
I'm not sure but you have data in handleSubmit as parameter but you don't pass it.
try this
You can call function like this:
handleSubmit=(data)=> {
updateUsers(this.state.users.id, data);
},
and call it
onSubmit={(data)=> this.handleSubmit(data)}
The problem was in ComponentDidMount(). The state was always null, had to change it to this
componentDidMount() {
fetchTheUsers(this.props.match.params.usersId)
.then(data => {
this.setState({
users: data
});
})
I did it that way initially because that's how it worked for my other update files. Hope this is useful to someone else.