I can't figure out why my form is populating my backend struct with empty strings. I can only get it to send the data correctly if I replace my onSubmit={handleSubmit} with method="POST" action="/createLink"
But even then it seems to send two form inputs.. one with empty values and one populated correctly. Backend is in Go.
const Home = () => {
//create state variables
const [media, setMedia] = React.useState([])
const [url, setURL] = React.useState(null)
const [headline, setHeadline] = React.useState(null)
const [description, setDescription] = React.useState(null)
const [type, setType] = React.useState(null)
const [duration, setDuration] = React.useState(null)
const [address, setAddress] = React.useState(null)
//HOOKS
//get media already submitted today to display on the front end
useEffect(() => {
fetch("/getMedia", {
method: "GET",
headers: {
"Content-Type" : "application/json",
},
})
.then((res) => res.json())
.then((data) => {
console.log(data)
setMedia(...media, data);
});
}, []);
//direct user back to home page
let navigate = useNavigate()
const handleHome = () => {
navigate("/")
}
//submit media
const handleSubmit = (e) => {
e.preventDefault()
fetch("/createLink", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
url: url,
headline: headline,
description: description,
type: type,
duration: duration,
address: address,
}),
})
console.log(address, "why")
handleHome()
}
//method="POST" action="/createLink"
return (
<>
<Div>
<H1>Submit External Media</H1>
<form onSubmit={handleSubmit} >
<Field>
<label htmlFor="url">URL Submission:</label>
<Input type="text" id="url" name="url" required onChange={(e) => {
setURL(e.target.value)
}}></Input>
</Field>
<Field>
<label htmlFor="headline">Headline:</label>
<Input type="text" id="headline" name="headline" required onChange={(e) => {
setHeadline(e.target.value)
}}></Input>
</Field>
<Field>
<label htmlFor="description">Description:</label>
<Input type="text" id="description" name="description" required onChange={(e) => {
setDescription(e.target.value)
}}></Input>
</Field>
<Field>
<label htmlFor="type">Type of Media:</label>
<select id="type" name="type" onChange={(e) => {
setType(e.target.value)
}}>
<option value="article" >Article</option>
<option value="tweet" >Tweet</option>
<option value="yt-video" >YouTube Video</option>
</select>
</Field>
<Field>
<label htmlFor="duration">Duration:</label>
<select id="duration" name="duration" onChange={(e) => {
setDuration(e.target.value)
}}>
<option value="15">15 Hours</option>
<option value="48" >48 Hours</option>
<option value="two-weeks" >Two Weeks</option>
</select>
</Field>
<Field>
<label htmlFor="location-lng">Street Address:</label>
<Input type="text" id="location-lng" name="location-lng" onChange={(e) => {
setAddress(e.target.value)
console.log(address)
}}></Input>
</Field>
<Button type="submit">SUBMIT</Button>
</form>
<h2>Media Submitted Today:</h2>
{ media.length >= 1 ? (
media.map((mediaItem) => {
const headline = mediaItem.headline
const description = mediaItem.description
const url = mediaItem.url
return <Media headline={headline} description={description} url={url} />
})
) : (
<>
<p>Looks like no external media was submitted yet today!</p>
</>
)}
</Div>
</>
)
}
export default Home;
One reason it might work with method="POST", but it is not working with your javascript function is that the default content type for a form is application/x-www-form-urlencoded.
In handleSubmit it sets the type to application/json, which your backend may not be expecting, and is not decoding properly.
Please consider this:
console.log everything before sending and validate input values are correct
The Input component seems to be Uncontrolled, validate if state if being set correctly
fetch is async and you're re-directing & pushing browser history state the user without waiting for the fetch call to end
Do this for advice 3:
//direct user back to home page
let navigate = useNavigate()
const handleHome = () => {
navigate("/")
}
//submit media
const handleSubmit = (e) => {
e.preventDefault()
fetch("/createLink", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
url: url,
headline: headline,
description: description,
type: type,
duration: duration,
address: address,
}),
}).then(() => handleHome()) // We are sure data was sent
}
Do this for advice 1:
//direct user back to home page
let navigate = useNavigate()
const handleHome = () => {
navigate("/")
}
//submit media
const handleSubmit = (e) => {
e.preventDefault()
const data = {
url,
headline,
description,
type,
duration,
address,
}
console.log({ data }) // Check the dev console
// JS Enhanced Object literals.
// https://www.sitepoint.com/es6-enhanced-object-literals
fetch("/createLink", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}).then(() => handleHome()) // We are sure data was sent
}
Related
I have a unique React Js form I am trying to validate before submission.
The issue is my onClick function is doing two other processes.
I am fetching data and redirecting. I am trying to validate the phone before any of the two processes takes action.
The phone/email/name field should not be empty, if empty the fetch or redirect must not take action.
By default, the number starts with +27.
Methods I have tried, Joi Form, React-Form-Hood, and statement
Here is the code:
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import Page from "react-page-loading";
function Checkout({ cart, setResponse }) {
const [contactPreference, setContactPreference] = useState("");
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const navigate = useNavigate();
const [spinner, setSpinner] = useState(false);
const handleSubmit = async () => {
setSpinner(true);
const token = "2c506c6b-d880-36bb-bdba-a035d1464b35";
const data = {
transactionReference: "string",
paymentMethod: "CreditCard",
checkoutOrderUrl: "http://www.test.com/",
user: {
name: name,
msisdn: "+27" + phone,
email: email,
},
payementMethodDetail: {
RedirectUrl: "http://localhost:3000/",
PurchaseEventWebhookUrl: "http://www.test.com",
},
bundle: cart.map((item) => ({
ProductCode: `${item.ProductCode}`,
Amount: item.amount,
CurrencyCode: item.currencyCode,
Quantity: item.quantity,
})),
};
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(data),
};
await fetch(
"https://api.flash-internal.flash-group.com/ecommerceManagement/1.0.0/api/checkout/",
requestOptions
)
.then((response) => response.json())
.then((response) => {
setResponse(response);
})
.then(() => {
setSpinner(false);
navigate("/payment");
});
};
console.log();
return (
<div className="App">
<div>
Name: <input onChange={(event) => setName(event.target.value)} />
<form className="form-inline">
<label className="my-1 mr-2" for="inlineFormCustomSelectPref">
How should we contact you ??
</label>
<select
onChange={(e) => {
setContactPreference(e.target.value);
}}
className="custom-select my-1 mr-sm-2"
id="inlineFormCustomSelectPref"
>
<option selected>Choose...</option>
<option value="phone">Phone Number</option>
<option value="email">Email</option>
</select>
{contactPreference === "phone" ? (
<input
placeholder="+27"
onChange={(event) => setPhone(event.target.value)}
/>
) : contactPreference === "email" ? (
<input
placeholder="Email"
onChange={(event) => setEmail(event.target.value)}
/>
) : (
<></>
)}
</form>
</div>
<button type="submit" onClick={handleSubmit}>
Proceed to Payment
</button>
{spinner && <Page loader={"spin"} color={"#b2fa00"} size={4}></Page>}
</div>
);
}
export default Checkout;
You can add the required attribute to input and select elements, put a ref on the form element, and in your handleSubmit function, use
const formValid = formRef.current?.reportValidity()
to get a boolean result and have the browser display hints on missing fields.
I have a dropdown of data from API. I want to be able to render the name of the HMO list but append the name and id of the SELECTED item.
const Form = () => {
const [hmo, setHmo] = useState([]);
const [selectedHmo, setSelectedHmo] = useState("");
const [file, setFile] = useState(null);
const getHmo = useCallback(async () => {
try {
const fetchHmo = await Axios.post("apiEndpoint/hmo");
const resultHmo = fetchHmo.data.data;
setHmo(resultHmo);
} catch (err) {
console.log(err);
}
}, []);
useEffect(() => {
getHmo();
}, [getHmo]);
const submitForm = (e) => {
e.preventDefault();
var enrollData = new FormData();
enrollData.append("file", file);
enrollData.append("hmo_id", selectedHmo);
enrollData.append("hmo_name", ?? )
Axios({
method: "POST",
url: "https://jsonplaceholder.typicode.com/posts",
headers: {
"Content-Type": "multipart/form-data",
},
data: enrollData,
})
.then((response) => {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
};
return (
<div className="form-container">
<Container>
<form onSubmit={submitForm}>
<div className="textfield">
<TextField
className="box"
select
name="hmo"
required
SelectProps={{
native: true,
}}
sx={{
width: "23ch",
}}
value={selectedHmo}
onChange={(e) => setSelectedHmo(e.target.value)}
>
<option>Select HMO</option>
{hmo?.map((res) => (
<option value={res.id} key={res.id}>
{res.name}
</option>
))}
</TextField>
<TextField
className="box"
required
name="file"
accept=".xlsx, .xls, .csv"
type="file"
label="Upload File (.csv, xls, xlsx)"
onChange={(e) => setFile(e.target.files[0])}
sx={{
width: "24ch",
}}
/>
</div>
<div className="btn">
<Button
type="submit"
variant="contained"
color="success"
>
Upload
</Button>
</div>
</div>
</form>
</Container>
</div>
);
};
export default Form;
I got confused at the point of appending the formdata fields as I can access the item id by value={res.id} from hmo map but I need to access the name and id. Then, append to form data before submission.
So the onChange property takes in any function. Right now it looks like this:
(e) => setSelectedHmo(e.target.value)
this can be written into something more dynamic for your use case.
(e) => {
const value = e.target.value
const name = e.target.name
const id = e.target.id
setSelectedHmo({value: value, name: name; id: id}) }
This way, selectedHmo stores value, name, and id. You can change this according to your use case and how you want to store things. Note this does change selectedHmo into an object, and is no longer a string.
If functions get long, you can even have the function declared prior to the return/render method in react, and pass that function name into the onChange handler.
Ex:
const Form = () =>{
[selectedHmo, setSelectedHmo] = useState({})
const selectedHmoChange = (e) ->{
const value = e.target.value
const name = e.target.name
const id = e.target.id
setSelectedHmo({value: value, name: name; id: id})
}
return (
<form onSubmit={submitForm}>
<div className="textfield">
<TextField
className="box"
select
name="hmo"
required
SelectProps={{
native: true,
}}
sx={{
width: "23ch",
}}
value={selectedHmo.value}
onChange = {selectHmoChange}
</TextField>
</form>
)
}
Now that you have all the data you need in selectedHmo, we can change the onSubmit function, since remember selectedHmo now looks like this: {id: id, name: name, value: value}
const submitForm = (e) => {
e.preventDefault();
var enrollData = new FormData();
enrollData.append("file", file);
enrollData.append("hmo_id", selectedHmo.id);
enrollData.append("hmo_name", selectedHmo.name )
}
I am fetching data from api and then store some detail in redux store and from redux store I am setting some values on browser localstorage.
I am using browser localstorage for conditional statement but because of undefined in or initial state inside the redux store my condtions are not executing.
My code:
const Login = () => {
const dispatch = useDispatch();
const { isAuth } = useSelector((state) => state.login);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const submitLoginForm = async (e) => {
e.preventDefault();
dispatch(loginPending());
const response = await fetch("https://localhost:44316/api/auth/login", {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
credentials: 'include',
body: JSON.stringify({
email,
password,
})
});
if(response.status === 200){
dispatch(loginSuccess());
debugger;
localStorage.setItem('auth',isAuth);
console.log(isAuth);
console.log(localStorage.getItem('auth'));
}
else{
dispatch(loginFailed());
}
};
if(JSON.parse(localStorage.getItem('auth')) === true){
return <Redirect to="/" />
}
return (
<React.Fragment>
<Navbar />
<div className="formContainer">
<h1>Login</h1>
<form className="loginForm" onSubmit={submitLoginForm}>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">
Email address
</label>
<input
type="email"
className="form-control"
aria-describedby="emailHelp"
onChange={(e) => setEmail(e.target.value)}
/>
<div id="emailHelp" className="form-text">
We'll never share your email with anyone else.
</div>
</div>
<div className="mb-3">
<label htmlFor="exampleInputPassword1" className="form-label">
Password
</label>
<input
type="password"
className="form-control"
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button type="submit" className="btn btn-primary">
Login
</button>
</form>
</div>
</React.Fragment>
);
};
My slice Contain:
loginSuccess: (state) => {
state.isLoading = false;
state.isAuth = true;
state.error = "";
}
I want to get isAuth value from redux store and then store it inside the browser local storage. But I am getting false value in my console
In the browser redux dev tool its showing the state change.
Following issue:
I am trying to create an authentication system in react, which let's a user login, sign up and reset the password. The problem is, I want to let the user go certain routes such as /profile, but display the content if he is logged in. If he is not logged in, I want to simply display the navigation bar and a small login component.
So far I tried something like this: (In order to make it more readable I left out the action-blocks)
const Profile = ({ history }) => {
const [values, setValues] = useState({
username: "",
email: "",
password: ""
});
const [isShowingAuth, setIsShowingAuth] = useState(null);
const { username, email, password } = values;
useEffect(() => {
if (!isLoggedIn()) {
setIsShowingAuth("login");
} else {
loadProfileData();
}
}, []);
const loadProfileData = () => {
const userEmail = isLoggedIn().email;
const token = getCookie("token");
axios({
method: "GET",
url: `${process.env.REACT_APP_BACKEND_URL}/user`,
headers: { Authorization: `Bearer ${token}` },
data: { userEmail }
})
.then(response => {
// Do something
})
.catch(error => {
// Do something
});
};
const handleChange = name => event => {
setValues({ ...values, [name]: event.target.value });
};
const handleUpdateUserSubmit = e => {
e.preventDefault();
const token = getCookie("token");
axios({
method: "PUT",
url: `${process.env.REACT_APP_BACKEND_URL}/user/update`,
headers: { Authorization: `Bearer ${token}` },
data: { username, password, email }
})
.then(response => {
// Do something
})
.catch(error => {
// Do something
}
store.addNotification({
...defaultSettings,
type: "danger",
title: "User update error",
message: error.response.data.errorMsg
});
});
};
const deleteAccount = () => {
const token = getCookie("token");
axios({
method: "DELETE",
url: `${process.env.REACT_APP_BACKEND_URL}/user`,
headers: { Authorization: `Bearer ${token}` }
})
.then(response => {
// Do something
})
.catch(error => {
// Do something
};
const showDeleteConfirmation = () => {
// Do something
};
return (
<Layout isShowingAuth={isShowingAuth}>
{isLoggedIn() && isLoggedIn().role === "member" ? (
<>
<ReactNotifications />
<h1 className='mt-5 pl-3'>Profile</h1>
<form className='form col-md-6 mt-3'>
<div className='form-group'>
<label>Name</label>
<input
type='text'
className='form-control'
value={username}
onChange={handleChange("username")}
/>
</div>
<div className='form-group'>
<label>Email</label>
<input
type='email'
className='form-control'
value={email}
onChange={handleChange("email")}
disabled
/>
</div>
<div className='form-group'>
<label>Password</label>
<input
type='password'
className='form-control'
value={password}
onChange={handleChange("password")}
placeholder='Enter your new password'
autoComplete='on'
/>
</div>
<button type='submit' className='btn btn-outline-primary' onClick={handleUpdateUserSubmit}>
Update User
</button>
<button type='button' className='btn btn-outline-danger ml-3' onClick={showDeleteConfirmation}>
Delete account
</button>
<button type='button' className='btn btn-outline-danger ml-3 mt-2 mt-md-0'>
Want to change your email?
</button>
</form>
{showAlert}
</>
) : null}
</Layout>
);
};
Basically I am checking through the isLoggedIn() method if the user is logged in. If he is, I want to render the content, if not I am returning only the layout. The layout contains the navigation bar.
The problem now is, that once the user logs in while being on the route /profile, the component doesn't remount, which means the useEffect(()=> {},[]) is not called again and my profile data is not loaded.
I already tried to refresh the page once logged in through history.push(history.location.pathname), but this doesn't trigger a remount either.
I'm no wondering how to properly trigger a remount after login, and if the way I am setting my authentication system up is secure or if there are better solutions.
Any help / feedback appreciated thanks :)
I must post {input} data to http://localhost:4000/prediction with Axios. But {input} turns undefined.
I am using const instead of class Main extends component. onChange, it sets form data.
const Main = ({ value, suggestions, auth: { user } }) => {
const [formData, setFormData] = useState("");
const [messages, setMessages] = useState([]);
const { input } = formData;
const onChange = e => setFormData(e.target.value);
const onSubmit = event => {
event.preventDefault();
setMessages(prevMsgs => [...prevMsgs, formData]);
console.log({ input });
Axios post.
axios
.post(
`http://localhost:4000/prediction`,
{ input },
{ crossdomain: true }
)
.then(res => {
console.log(res.data);
//setMessages(prevMsgs => [...prevMsgs, formData]);
})
.catch(error => {
console.log(error.message);
});
};
Return (form) with onSubmit, onChange.
return (
<div className="true">
<br />
<form noValidate onSubmit={e => onSubmit(e)}>
<div className="input-group mb-3">
<input
name="input"
type="text"
className="form-control"
placeholder="Type text"
onChange={e => onChange(e)}
/>
)}
<div className="input-group-append">
<button className="btn btn-outline-secondary">Send</button>
</div>
</div>
</form>
</div>
);
};
As I have mentioned in the comment section formData is a string as I see which does not have a property called input what you try to destructure and that's why it is undefined always.
If you really need that format for axios then you can try change the structure of formData with useState as the following first:
const [formData, setFormData] = useState({input: null});
Then maybe you can try updating as:
const onChange = e => setFormData({input: e.target.value});
I hope that helps!