I have several pages on my react app.
Edit, Delete, Create, Home
I am using React Router for navigation. I found this issue:
When I refresh a page that has :id in its params like "/games/edit/1" or "/games/details/2" my style.css is not loading. Instead in its it loads "You need to enable JavaScript to run this app." when I inspect the Networking tab in my browser.
NOTE : My style.css is included in the index.html file
Here is my Edit Component :
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { editGame, getGame } from 'services/api/games';
import { getAccessToken } from 'utils/userToken';
export const Edit = () => {
const navigate = useNavigate();
const { id } = useParams();
const [formData, setFormData] = useState({
category: '',
title: '',
maxLevel: '',
imageUrl: '',
summary: '',
});
useEffect(() => {
getGame(id).then(({ category, title, maxLevel, imageUrl, summary }) => {
let newForm = {
category,
title,
maxLevel,
imageUrl,
summary,
};
setFormData((formData) => newForm);
});
}, [id]);
function onChange(ev) {
const name = ev.target.name;
const value = ev.target.value;
const newForm = {
...formData,
[name]: value,
};
setFormData((formData) => newForm);
}
async function onSubmite(ev) {
ev.preventDefault();
const token = await getAccessToken();
const response = await editGame(id, token, formData);
if (response.code === 403) {
return window.alert('You are not authorized');
}
navigate(`/games/${id}`);
}
return (
// <!-- Edit Page ( Only for the creator )-->
<section id="edit-page" className="auth">
<form id="edit" onSubmit={onSubmite}>
<div className="container">
<h1>Edit Game</h1>
<label htmlFor="leg-title">Legendary title:</label>
<input
type="text"
id="title"
name="title"
value={formData.title}
onChange={onChange}
/>
<label htmlFor="category">Category:</label>
<input
type="text"
id="category"
name="category"
value={formData.category}
onChange={onChange}
/>
<label htmlFor="levels">MaxLevel:</label>
<input
type="number"
id="maxLevel"
name="maxLevel"
min="1"
value={formData.maxLevel}
onChange={onChange}
/>
<label htmlFor="game-img">Image:</label>
<input
type="text"
id="imageUrl"
name="imageUrl"
value={formData.imageUrl}
onChange={onChange}
/>
<label htmlFor="summary">Summary:</label>
<textarea
name="summary"
id="summary"
onChange={onChange}
value={formData.summary}
></textarea>
<input
className="btn submit"
type="submit"
value="Edit Game"
/>
</div>
</form>
</section>
);
};
Try importing your CSS into the index.js or App.js file (not sure your folder structure) instead of the in your index.html file.
import '../some/route/style.css';
Related
First.js
import { useState } from "react";
import { Link } from "react-router-dom";
const First = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
}
return (
<div className="First">
<h1>This is First Input Page</h1>
<form onSubmit={handleSubmit}>
<dd>data 1</dd>
<input
type="text"
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
></input>
<dd>data 2</dd>
<input
type="text"
value={phone}
onChange={(e) =>
setPhone(e.target.value)
}
required
></input><br/>
<Link to={'/second'} state={{ state: { name : name , phone : phone } }}><button>submit</button></Link>
</form>
</div>
);
}
export default First;
I try to send Object data using Link/state to another component.
Second.js
import {useLocation} from 'react-router-dom';
const Second = () => {
const location = useLocation();
console.log(location.state);
console.log(location.state.name);
return (
<div className="Second">
<h1>This is Second Input Page</h1>
<form>
<dd>data 3</dd>
<input></input>
<dd>data 4</dd>
<input></input><br/>
<button>submit</button>
</form>
</div>
);
}
export default Second;
However, while I can access to (location.state), I can not access to (location.state.name). Why is that?
Output
state: {name: 'Myname', phone: 'myphone'}
[[Prototype]]: Object
--------------------
undefined
The output shows that the code line "console.log(location.state);" works, but to the "console.log(location.state.name);", it shows undefined.
It's because you passed an object with state as the root property, i.e.
state={{ state: { name: name, phone: phone } }}
so to access it in the receiving route it is location.state.state.name.
You really don't need to nest the data you want to pass under a state property when using the Link component, it's not quite the same as when using the navigate function.
<Link to="/second" state={{ name, phone }}>
<button>submit</button>
</Link>
It may also be considered semantically incorrect HTML to nest a button element within an anchor tag (via Link) element. Use the useNavigate hook and issue an imperative navigation action from the form's submit handler. In this case the navigate function's second argument, the options object, *does* expect the state to be passed under the state` property.
Example:
import { Link, useNavigate } from "react-router-dom";
const First = () => {
const navigate = useNavigate();
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
navigate("/second", { state: { name, phone } });
};
return (
<div className="First">
<h1>This is First Input Page</h1>
<form onSubmit={handleSubmit}>
<dd>data 1</dd>
<input
type="text"
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
/>
<dd>data 2</dd>
<input
type="text"
value={phone}
onChange={(e) =>
setPhone(e.target.value)
}
required
/>
<br/>
<button>submit</button>
</form>
</div>
);
};
export default First;
I am building a social media app with MERN stack. The issue is that the profile picture and cover picture is not changing the state.
The API is working fine but after debugging I found that use state is not accepting the new state only in initial state i.e. null.
The handleChange functionality is working well and yet after updating resulting empty object in the formData
import React, {use State } from 'react';
import { Modal, useMantineTheme } from '#mantine/core';
import { use Dispatch, use Selector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { uploadImage } from '../../actions/uploadAction';
import {updateuser} from '../../actions/userAction';
const ProfileModal = ({modalOpen,setModalOpen,data}) => {
const theme = useMantineTheme();
const {password,...other} = data;
const [formData,setFormData] = useState(other);
const [profileImage,setProfileImage] = useState(null);
const [coverImage,setCoverImage] = useState(null);
const dispatch = useDispatch();
const params = useParams();
const {user} = useSelector((state)=>state.authReducer.authData);
const handleChange = (e) =>{
setFormData({...formData, [e.target.name]:e.target.value})
console.log(formData)
}
const onImageChange = (event)=>{
if (event.target.files && event.target.files[0]) {
let image = event.target.files[0]
console.log(image)
event.target.name==="profilePicture"?setProfileImage(image):setCoverImage(image)
console.log(profileImage)
console.log(coverImage)
}
}
const handleSubmit =(e)=>{
e.preventDefault();
let userData = formData;
console.log(userData);
if(profileImage){
const data = new FormData();
const fileName = Date.now() + profileImage.name;
data.append("name",fileName);
data.append("file",profileImage);
userData.profilePicture = fileName;
console.log("profile",data)
}
try {
dispatch(uploadImage(data))
} catch (error) {
console.log(error)
}
if (coverImage) {
const data = new FormData();
const fileName = Date.now() + coverImage.name;
data.append("name",fileName);
data.append("file",coverImage);
userData.coverPicture = fileName;
}
try {
dispatch(uploadImage(data))
} catch (error) {
console.log(error)
}
dispatch(updateuser(params.id,userData))
console.log(userData)
setModalOpen(false)
}
return (
<Modal
overlayColor={theme.colorScheme === 'dark' ? theme.colors.dark[9] : theme.colors.gray[2]}
overlayOpacity={0.55}
overlayBlur={3}
size = '55%'
opened = {modalOpen}
onClose ={()=>setModalOpen(false)}>
<form className='infoForm'>
<h3>Your Info</h3>
<div>
<input type='text' className='infoInput' name='firstName'
placeholder='First Name' onChange={handleChange} value={formData.firstName} />
<input type='text' className='infoInput' name='lastName'
placeholder='Last Name' onChange={handleChange} value={formData.lastName} />
</div>
<div>
<input type='text' className='infoInput' name='worksAt'
placeholder='Work Location' onChange={handleChange} value={formData.worksAt}/>
</div>
<div>
<input type='text' className='infoInput' name='livesIn'
placeholder='Lives In' onChange={handleChange} value={formData.livesIn} />
<input type='text' className='infoInput' name='country'
placeholder='Country' onChange={handleChange} value={formData.country} />
</div>
<div>
<input type='text' className='infoInput' name='relationship' placeholder='Relationship Status' onChange={handleChange}
value={formData.relationship} />
</div>
<div>
profilePicture
<input type='file' name="profilePicture" onChange={onImageChange} alt=""/>
cover Image
<input type='file' name="coverPicture" onChange={onImageChange} alt="" />
</div>
<button className='button infoButton' onClick={handleSubmit}>Update</button>
</form>
</Modal>
);
}
export default ProfileModal
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
Which is why we have useEffect, a built-in React hook that activates a callback when one of it's dependencies have changed.
Example:
useEffect(() => {
console.log(formData)
// Whatever else we want to do after the state has been updated.
}, [formData])
This console.log will run only after the state has finished changing and a render has occurred.
Note: "formData" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info about this.
Change your first import line from {use State} to {useState}...no whitespace import React, {useState } from 'react';
I have a form with a button to submit it, and I also use the same button to route to another page.
the button work in routing and when I remove the Navlink tag it submits the form. but it does not submit it when the Navlink tags are there and does not show the validation error msgs as well it just route the page.
any help on how to get the two actions to work?
here is my code
import react, { Component, useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faEyeSlash, faEye } from "#fortawesome/free-solid-svg-icons";
import './sign.css';
const Sign = () => {
//state to be sent to backend
const intialValues = { firstname: "", lastname: "", mobile: "", email: "", password: "", cpassword: "" };
const [formValues, setFormValues] = useState(intialValues);
const [formErrors, setFormErrors] = useState({});
const [isSubmit, setIsSubmit] = useState(false);
const handleChange = (e) => {
console.log(e.target.value);
const { name, value } = e.target;
setFormValues({ ...formValues, [name]: value });
}
const handleSubmit = (err) => {
err.preventDefault();
setFormErrors(validate(formValues));
setIsSubmit(true);
}
useEffect(() => {
if (Object.keys(formErrors).length === 0 && isSubmit) {
console.log(formValues);
}
}, [formErrors])
const validate = (values) => {
const errors = {};
if (!values.firstname) {
errors.firstname = 'firstname is required!';
}
if (!values.lastname) {
errors.lastname = 'lastname is required!';
}
return errors;
}
return (
<div className='signup'>
<form onSubmit={handleSubmit} >
<div className="container">
<h1>Sign Up</h1>
<div className="name">
<div>
<input
type="text"
placeholder="First name"
name="firstname"
id='firstName'
value={formValues.firstname}
onChange={handleChange}
/>
</div>
<div>
<input
type="text"
placeholder="Last name"
name="lastname"
value={formValues.lastname}
onChange={handleChange}
/>
</div>
</div>
<p className='errorMsg'>{formErrors.firstname}</p>
<p className='errorMsg'>{formErrors.lastname}</p>
<br />
<div className="clearfix">
<NavLink to='/profileclient'>
<button type="submit" className="signupbtn">Sign Up</button>
</NavLink>
</div>
</div>
</form>
</div>
)
}
export default Sign;
then I tried to use useNavigate so I modified these lines, and it does navigate to the other page but in the console, it gives me a warning "Form submission canceled because the form is not connected", it does not log the state objects.
const navigate = useNavigate();
<div className="clearfix">
<button type="submit" className="signupbtn" onClick={() => { navigate('/profileclient') }}>Sign Up</button>
</div>
I discovered the solution.
the main problem was that I tried to trigger functions at the same time by clicking on a button, while the submission needs time first before the navigation could happen, this is why it did not submit the form but only navigated. in order to solve the ambiguous behavior, I have put the navigation function call in if statement to make sure the submission is done or not before the navigation is executed. I have also changed the place where I call the navigate function, rather than calling it on the button onClick attribute, I placed it in the useEffect where I check if there are no errors you can submit the form and after submitting it you can navigate to another page.
this is the part of my code that made it work well.
if (isSubmit) {
return (navigate('/profileclient'));
}
and here is my full code
import react, { Component, useState, useEffect } from 'react';
import { NavLink, useNavigate } from 'react-router-dom';
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faEyeSlash, faEye } from "#fortawesome/free-solid-svg-icons";
import './sign.css';
const SignC = () => {
const navigate = useNavigate();
//state to be sent to backend
const intialValues = { firstname: "", lastname: "", mobile: "", email: "", password: "", cpassword: "" };
const [formValues, setFormValues] = useState(intialValues);
const [formErrors, setFormErrors] = useState({});
const [isSubmit, setIsSubmit] = useState(false);
const [passwordShown, setPasswordShowen] = useState(false);
const [cPasswordShown, setCPasswordShowen] = useState(false);
const [eyeShowen, setEyeShowen] = useState(false);
const [cEyeShowen, setCEyeShowen] = useState(false);
const handleChange = (e) => {
console.log(e.target.value);
const { name, value } = e.target;
setFormValues({ ...formValues, [name]: value });
}
const handleSubmit = (err) => {
err.preventDefault();
setFormErrors(validate(formValues));
setIsSubmit(true);
}
useEffect(() => {
if (Object.keys(formErrors).length === 0 && isSubmit) {
console.log(formValues);
if (isSubmit) {
return (navigate('/profileclient'));
}
}
}, [formErrors])
const validate = (values) => {
const errors = {};
const regex = /^[^\s#]+#[^\s#]+\.[^\s#]{2,}$/i;
if (!values.firstname) {
errors.firstname = 'firstname is required!';
}
if (!values.lastname) {
errors.lastname = 'lastname is required!';
}
if (!values.mobile) {
errors.mobile = 'mobile is required!';
}
if (!values.email) {
errors.email = 'email is required!';
} else if (!regex.test(values.email)) {
errors.email = 'this is not a valid email format!'
}
if (!values.password) {
errors.password = 'password is required!';
} else if (values.password.length < 4) {
errors.password = 'password must be more than 4 characters';
} else if (values.password.length > 10) {
errors.password = 'password must not exceeds 10 characters';
}
if (!values.cpassword) {
errors.cpassword = 'password confirmation is required!';
} else if (values.cpassword != values.password) {
errors.cpassword = 'confirmation password does not match!';
}
return errors;
}
const togglePassword = () => {
setPasswordShowen(!passwordShown);
toggleEye();
}
const toggleCPassword = () => {
setCPasswordShowen(!cPasswordShown);
toggleCEye();
}
const toggleEye = () => {
setEyeShowen(!eyeShowen);
}
const toggleCEye = () => {
setCEyeShowen(!cEyeShowen);
}
return (
<div className='signup'>
<form onSubmit={handleSubmit} >
<div className="container">
<h1>Sign Up</h1>
<div className="name">
<div>
<input
type="text"
placeholder="First name"
name="firstname"
id='firstName'
value={formValues.firstname}
onChange={handleChange}
/>
</div>
<div>
<input
type="text"
placeholder="Last name"
name="lastname"
value={formValues.lastname}
onChange={handleChange}
/>
</div>
</div>
<p className='errorMsg'>{formErrors.firstname}</p>
<p className='errorMsg'>{formErrors.lastname}</p>
<br />
<div>
<input
type="text"
placeholder="Business mobile number"
name="mobile"
value={formValues.mobile}
onChange={handleChange}
/>
<p className='errorMsg'>{formErrors.mobile}</p>
<br />
<input
type="text"
placeholder="Email Adress"
name="email"
value={formValues.email}
onChange={handleChange}
/>
<p className='errorMsg'>{formErrors.email}</p>
<br />
<div className="password">
<input
type={passwordShown ? 'text' : 'password'}
placeholder="Password"
name="password"
id='password'
value={formValues.password}
onChange={handleChange}
/>
<FontAwesomeIcon
icon={eyeShowen ? faEye : faEyeSlash}
id='togglePassword'
onClick={togglePassword}
/>
<p className='errorMsg'>{formErrors.password}</p>
<br />
<input
type={cPasswordShown ? 'text' : 'password'}
placeholder="Confirm Password"
name="cpassword"
id='Cpassword'
value={formValues.cpassword}
onChange={handleChange}
/>
<FontAwesomeIcon
icon={cEyeShowen ? faEye : faEyeSlash}
id='toggleCPassword'
onClick={toggleCPassword}
/>
<p className='errorMsg'>{formErrors.cpassword}</p>
</div>
</div>
<br />
<div className="checkbox">
<label>
<input type="checkbox" className="check" />i’ve read and agree with <a href="url" >Terms of service</a>
</label>
</div>
<div className="clearfix">
<button type="submit" className="signupbtn">Sign Up</button>
</div>
</div>
</form >
</div >
)
}
export default SignC;
I am trying to make a button which submit the form and then navigate
to another page.
The important distinction here is the then. When you wrap a link around a button both elements are clicked.
<NavLink to='/profileclient'>
<button type="submit" className="signupbtn">Sign Up</button>
</NavLink>
The link tries to navigate at the same time the form is processing its onSubmit handler.
If you want to conditionally navigate at the after submitting the form then you need to move the navigation logic there. Use the useHistory or useNavigate (depending on react-router-dom version, v5 the former, v6 the latter). There's also no need to store an isSubmit state, you can simply check the validation result in the submit handler and conditionally set the form errors or navigate to the next page.
Example:
const Sign = () => {
const navigate = useNavigate();
...
const handleSubmit = (event) => {
event.preventDefault();
const errors = validate(formValues);
if (Object.values(errors).length) {
setFormErrors(errors);
} else {
navigate("/profileclient");
}
};
const validate = (values) => {
const errors = {};
...
return errors;
};
return (
<div className="signup">
<form onSubmit={handleSubmit}>
<div className="container">
<h1>Sign Up</h1>
...
<div className="clearfix">
<button type="submit" className="signupbtn">
Sign Up
</button>
</div>
</div>
</form>
</div>
);
};
I have a form in a react project that is behaving a bit strange. I have e.preventDefault() attatched to my submit button but for some reason the page is still refreshing each time the button is clicked. Could someone please help me figure out why this is happening? Here is the Component in question:
import React, { useState } from 'react';
import './PostForm.css';
import axios from 'axios'
const PostForm = () => {
const [posts, setPost] = useState({
body: '',
author: 'Michael',
});
const onChange = (e) =>{
setPost({...posts, [e.target.name] : e.target.value})
}
const sendPost = (e) => {
e.preventDefault()
try {
const res = axios.post('http://localhost:4000/post', posts);
console.log(res)
} catch (error) {
console.log(error)
}
}
return (
<form className="formContainer">
<div className="form">
<textarea className='formBody' type="text" placeholder="What's new?" name='body' onChange={onChange} />
<button onSubmit={sendPost}>Share</button>
</div>
</form>
);
};
export default PostForm;
onSubmit should be in the form tag. And change the button to input and it's type as submit.
<form className="formContainer" onSubmit={sendPost}>
<div className="form">
<textarea className='formBody' type="text" placeholder="What's new?" name='body' onChange={onChange} />
<input type="submit" value="Share" />
</div>
</form>
I have a CRUD React app where you can create articles. There is a 'title', 'content' and 'image'.
I have a collection in Cloud Firestore called 'articles'. The documents have the fields: 'title' and 'content' so every time I create an article in my react app, a new document is created.
The next step was to be able to add images to the articles.
I managed to connect to Firebase and upload an image to Firebase Storage from my React page. However how do I connect the images to their articles? Like, how do I make the connection between Cloud Firestore and Storage?
Do I create an extra field in my documents called 'images'? But then how do I reference it?
I also want to try and avoid duplicates.
UploadImage.js
import React, { useState, Component } from "react";
import { storage } from "../Firebase";
function UploadFile() {
const [image, setImage] = useState(null);
const [url, setUrl] = useState("");
const [progress, setProgress] = useState(0);
const handleChange = e => {
if (e.target.files[0]) {
setImage(e.target.files[0]);
}
};
const handleUpload = () => {
const uploadTask = storage.ref(`images/${image.name}`).put(image);
uploadTask.on(
"state_changed",
snapshot => {
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
setProgress(progress);
},
error => {
console.log(error);
},
() => {
storage
.ref("images")
.child(image.name)
.getDownloadURL()
.then(url => {
setUrl(url);
});
}
);
};
console.log("image: ", image);
return (
<div>
<progress value={progress} max="100" />
<br />
<br />
<input type="file" onChange={handleChange} />
<button onClick={handleUpload}>Upload</button>
<br />
{url}
<br />
<img src={url || "http://via.placeholder.com/300"} alt="firebase-image" />
</div>
);
}
export default UploadFile;
This is my 'Add Article' form-->
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: ''
};
}
onChange = (e) => {
const state = this.state
state[e.target.name] = e.target.value;
this.setState(state);
}
onSubmit = (e) => {
e.preventDefault();
const { title, content } = this.state;
this.ref.add({
title,
content
}).then((docRef) => {
this.setState({
title: '',
content: ''
});
this.props.history.push("/")
})
.catch((error) => {
console.error("Error adding document: ", error);
});
}
render() {
const { title, content } = 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>
<UploadFile />
<button type="submit" className="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
export default AddArticle;
It is actually pretty simple, to achieve this follow steps below:
After uploading your image to firebase storage you should get download url for that particular image (I linked firebase documentation link below on how to achieve that).
https://firebase.google.com/docs/storage/web/download-files
If you successfully fetched download url from firebase storage now it is time to create separate field under your document named such as "postImageUrl" (you can name whatever you want) and save download url under this field.
Now you can show images using this download url in your page using busboy library
https://www.npmjs.com/package/busboy
Note: I am an Android Developer so I tried my best to help. If you have further questions don't hesitate to ask