Wondering how to debug (Or where to get started) on my current Axios issue. I have a Next.js project (12.3) and have an axios interceptor hook that handles all of my internal requests. The interceptor works on every action /on every page however this certain page /me/settings/password&security/email/verify.
This specific action is being used on several other pages with no issue. Evenb copying the entire page from a working one freezes the tab. It hangs in preflight for a few minutes then I get an iteration of [Violation] 'submit' handler took <N>ms.
Here is my entire page:
export default function VerifyEmailForm() {
const router = useRouter();
const user: UserState = useSelector((root: RootState) => root.user);
const [apiError, setApiError] = useState("");
const [schema, setSchema] = useState<any>();
const [resubmit, setResubmit] = useState(false);
const [updateEmail, setUpdateEmail] = useState(false);
const [submitSuccsess, setSubmitSuccess] = useState(false);
const [dispatchAxios, { success, loading, error }] = useAxios();
const initialValues = {
email: user?.email ? user?.email : "",
};
const handleSubmit = async (values: any) => {
if (!user.email || updateEmail) {
await dispatchAxios(
updateUser({
action: [
{
key: "email",
value: values?.email,
},
],
})
);
}
await dispatchAxios(requestEmailVerification(values?.email)).then((res) => {
if (res?.data?.error) {
setApiError(res?.data?.error);
} else {
setResubmit(true);
setSubmitSuccess(true);
}
});
};
useEffect(() => {
const yup = import("yup").then((yup) =>
setSchema(
yup.object().shape({
email: yup
.string()
.email("Must be a valid email")
.required("Email is required"),
})
)
);
}, []);
useEffect(() => {
if (error && !loading) {
setApiError(error?.message);
}
}, [loading, success, error]);
return (
<Formik
validationSchema={schema}
initialValues={initialValues}
onSubmit={handleSubmit}
validateOnBlur={true}
>
{({ handleSubmit, setFieldValue, errors, values, touched, isValid }) => {
return (
<Form
onSubmit={handleSubmit}
onBlur={() =>
setTimeout(() => {
setApiError("");
}, 200)
}
>
<div className="flex flex-wrap gap-9 pb-2.5 ">
{!user.email || updateEmail ? (
<div className="w-full relative">
<div className="relative w-full">
<Field
styles="bg-transparent text-center"
name="email"
type="email"
placeholder="Email address"
component={InputField}
error={errors.email}
/>
{values.email && (
<button
type="button"
onClick={() => setFieldValue("email", "")}
className={`flex items-center justify-center bg-background-secondary p-0.5 rounded-full w-4 h-4 absolute bottom-2 ${
errors.email ? "right-8 " : "right-2.5 "
}`}
>
<span className="sr-only">Clear Field</span>
<XMarkIcon
className="text-deactive w-2.5 "
aria-hidden="true"
/>
</button>
)}
</div>
<InputError
error={errors.email as string}
touched={touched?.email as boolean}
styles={"flex justify-center"}
/>
</div>
) : (
<></>
)}
<div className="w-full flex flex-col justify-center items-center">
<Button
type="submit"
disabled={!isValid}
classes={`w-full text-white p-3 rounded-2xl mx-8 ${
errors.email || !values.email.length
? "border-transparent text-deactive"
: "border-2 border-primary"
}`}
label={resubmit ? "Resend Verification" : "Verify"}
/>
{user.email && (
<Button
type="button"
classes={`w-full text-white p-3 rounded-2xl mx-8 border-transparent`}
label="Update Email"
onClick={() => setUpdateEmail(true)}
/>
)}
<Button
type="button"
classes={`w-full text-white p-3 rounded-2xl mx-8 border-transparent`}
label="Cancel"
onClick={() => router.back()}
/>
</div>
</div>
<ErrorAlertModal errors={apiError ? [apiError] : undefined} />
<SuccessAlertModal success={submitSuccsess} />
</Form>
);
}}
</Formik>
);
}
And Here is my Axios hook:
export function useAxios(
internal: boolean = true
): [DispatchAxios, IAxiosState] {
const { data: session, status } = useSession();
const dispatch = useDispatch();
const isMounted = useRef(true);
const user: UserState = useSelector((root: RootState) => root.user);
const env = process.env.NODE_ENV;
const { logoutUser } = useLogout();
const client = axios.create({
// baseURL: process.env.API_URL,
});
client.interceptors.request.use((config: AxiosRequestConfig) => {
config.headers = config.headers || {};
if (!config.headers["Content-Type"]) {
config.headers["Content-Type"] = "application/json";
}
if (!config.headers["Ocp-Apim-Subscription-Key"] && env === "production") {
config.headers[
"Ocp-Apim-Subscription-Key"
] = `${process.env.NEXT_PUBLIC_API_GATEWAY_KEY}`;
}
// Internal requests need tokens and other parameters added in.
if (internal) {
if (session?.accessToken) {
config.headers.Authorization = `Bearer ${session.accessToken}`;
}
}
return config;
});
const [state, setState] = useState<IAxiosState>({
loading: false,
error: null,
response: null,
success: false,
});
const dispatchAxios: DispatchAxios = useCallback(
(params: IAxiosAction) => {
const action: IAxiosAction = {
type: params.type,
config: params.config,
batchOrder: params.batchOrder || [],
};
if (params.data) {
action.data = params.data;
}
dispatch({
...action,
loading: true,
});
setState({
...state,
error: null,
response: null,
loading: true,
success: false,
});
return client(params.config)
.then((response: AxiosResponse) => {
// dispatch must come before setState
if (response.data.err === "INVALID TOKEN") {
throw action;
}
dispatch({
...action,
response,
loading: false,
});
if (isMounted.current) {
setState({
...state,
loading: false,
success: true,
error: null,
response,
});
}
return {
success: true,
error: null,
response,
};
})
.catch((error: AxiosError) => {
const originalReq = error.config;
// Token refresh failed, log user out.
if (
originalReq?.url ===
process.env.NEXT_PUBLIC_AUTH_REFRESH_API_ENDPOINT
) {
// logoutUser();
}
// dispatch must come before setState
dispatch({
...action,
error,
loading: false,
success: false,
});
if (isMounted.current) {
setState({
...state,
loading: false,
success: false,
response: null,
error,
});
}
return {
success: false,
response: null,
error,
};
});
},
[isMounted, dispatch, state, client]
);
useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, [state]);
return [dispatchAxios, state];
}
The updateUser action is being used in several other parts of the app as the user is able to change and update parts of their profile. As you can see the email is being targeted in this instance. We use this same request in another page /me/settings/password&security/email and it resolves just fine no freezing no hanging. Im convin
It was my own import of moment.js. Dynamically importing it in a wrapping component.
Somehow it cased the issue not sure why. :)
Related
1.After I came to profile page that show my profile details my console log shows POST http://localhost:3000/profileEdit 500 (Internal Server error)
2.I change my activity drinks about topics and then refresh page it shows nothing like it didn't save at all except image
I debug by myself then tried to find solution in reddit, quora, stackoverflow 5 day but can't find it So please could you help me a bit
this is frontend
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { useCookies } from "react-cookie";
import { Link } from "react-router-dom";
import axios from "axios";
import {
ref, uploadBytesResumable, getDownloadURL, getStorage
} from "firebase/storage";
import { profileInit } from "../redux/action";
import "./profileEdit.css";
import styled from "styled-components";
const Avatar = styled.div`
width: 250px;
height: 250px;
border-radius: 50%;
background-size: cover;
background-position: center;
cursor: pointer;
`;
function ProfileEdit(props) {
const [cookies, removeCookies] = useCookies([
"userName",
"userNickname",
]);
const [activity, setActivity] = useState("");
const [drinks, setDrinks] = useState("");
const [topics, setTopics] = useState("");
const [about, setAbout] = useState("");
const [url, setUrl] = useState("./images/UploadPic.svg");
const [save, setSave] = useState("");
const id = cookies.userName;
const { profileInit, user } = props;
const [image, setImage] = useState(null);
function patchData(event) {
event.preventDefault();
axios
.patch("/users/profile", {
activity,
drinks,
topics,
about,
id,
})
.then(({ data }) => {
if (data.success) {
setSave("Changes were saved");
} else {
setSave(data.err);
}
});
const storage = getStorage();
const storageRef = ref(storage, `images/${cookies.userName || "./images/infoUser.svg"}`);
const uploadTask = uploadBytesResumable(storageRef, image);
uploadTask.on("state_changed", undefined, undefined, () => {
getDownloadURL(storageRef).then((url) => {
setUrl(url);
});
});
if (setUrl !== null || setUrl == null) {
axios
.patch("/users/profile", {
activity,
drinks,
topics,
about,
id,
avatar: url,
})
.then(({ data }) => {
if (data.success) {
setSave("Saved");
} else {
setSave(data.err);
}
});
}
}
function handleChangeAbout(event) {
event.preventDefault();
setAbout(event.target.value);
}
function handleChangeDrinks(event) {
event.preventDefault();
setDrinks(event.target.value);
}
function handleChangeTopics(event) {
event.preventDefault();
setTopics(event.target.value);
}
function handleChangeActivity(event) {
event.preventDefault();
setActivity(event.target.value);
}
function LogOut() {
user.id = null;
removeCookies("userName");
removeCookies("userNickname");
}
useEffect(() => {
const storage = getStorage();
getDownloadURL(ref(storage, `images/${cookies.userName}`))
.then((url) => {
setUrl(url);
});
axios
.post('/users/profileEdit', {
id,
})
.then(({ data }) => {
setActivity(data.profileId.activity);
setDrinks(data.profileId.drinks);
setAbout(data.profileId.about);
setTopics(data.profileId.topics);
profileInit(data.profileId);
});
}, [profileInit, id]);
function photoDownload(e) {
if (e.target.files[0]) {
setImage(e.target.files[0]);
const storage = getStorage();
const storageRef = ref(storage, `images/${cookies.userName}`);
const uploadTask = uploadBytesResumable(storageRef, image);
uploadTask.on(
"state_changed",
() => {
setUrl("./loading.gif");
},
(error) => {
console.log(error);
},
() => {
getDownloadURL(storageRef)
.then((url) => {
setUrl(url);
});
},
);
}
}
return (
<>
<div className="profile-container">
<div style={{ alignSelf: "center" }}>
<label htmlFor="file-input">
<Avatar style={{ backgroundImage: `url(${url})` }} />
</label>
<input id="file-input" type="file" onChange={photoDownload} />
</div>
<form onSubmit={patchData} className="edit">
<span
style={{ textShadow: "none", marginBottom: "8px", color: "#fff" }}
>
Activity:
</span>
<label>
<input
value={activity}
onChange={handleChangeActivity}
type="text"
name="activity"
className="profileInput"
required
/>
</label>
<span
style={{ textShadow: "none", marginBottom: "8px", color: "#fff" }}
>
Topics:
</span>
<label>
<input
value={topics}
onChange={handleChangeTopics}
type="text"
name="topics"
className="profileInput"
required
/>
</label>
<span
style={{ textShadow: "none", marginBottom: "8px", color: "#fff" }}
>
About:
</span>
<label>
<input
value={about}
onChange={handleChangeAbout}
type="text"
name="about"
className="profileInput"
required
/>
</label>
<span
style={{ textShadow: "none", marginBottom: "8px", color: "#fff" }}
>
Drinks:
</span>
<label>
<input
value={drinks}
onChange={handleChangeDrinks}
type="text"
name="drinks"
className="profileInput"
required
/>
</label>
<button
style={{ margin: "0 auto" }}
className="chatButton"
>
{" "}
Save changes
{" "}
</button>
<div style={{ marginTop: "15px", color: "#fff" }}>
{" "}
{save}
</div>
</form>
<div className="quitEdit" style={{ margin: "0 auto" }}>
<Link to="/listUsers" style={{ position: "relative" }}>
<img src="./images/back.svg" width="100" height="100" alt="BackToListPage" title="BackToListPage" />
</Link>
</div>
<div className="exit" style={{ margin: "0 auto" }}>
<Link to="/login" onClick={LogOut} style={{ position: "relative" }}>
<img src="./images/exit.svg" width="100" height="100" alt="Logout" title="Logout" />
</Link>
</div>
</div>
</>
);
}
const mapStateToProps = (state) => ({
profileId: state.user.profileId,
user: state.user,
err: state.error,
});
const mapDispatchToProps = (dispatch) => ({
profileInit: (profileId) => dispatch(profileInit(profileId)),
});
export default connect(mapStateToProps, mapDispatchToProps)(ProfileEdit);
my backend code for /users/profile and /users/profileEdit
router.patch('/profile', async (req, res) => {
const {
activity,
topics,
about,
drinks,
avatar,
id,
} = req.body;
const response = await Profile.updateOne({ person: id }, {
activity, topics, about, drinks, avatar
});
if (response) {
res.send({ success: true });
} else {
res.send({ success: false, err: 'Try again' });
}
});
router.post('/profileEdit', async (req, res) => {
const { id } = req.body;
const response = await Profile.findOne({ person: id });
if (response) {
res.send({ profileId: response });
} else {
res.status(500).send({ err: 'Something went wrong' });
}
});
Edit post here -> modelProfile.js
const mongoose = require('mongoose');
const profileSchema = new mongoose.Schema({
person: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Person',
},
name: {
type: String,
required: true,
minlength: 1,
},
DoB: {
type: Date,
required: true,
},
activity: {
type: String,
required: true,
minlength: 1,
},
about: {
type: String,
minlength: 1,
},
topics: String,
drinks: String,
avatar: String,
latitude: Number,
longitude: Number,
},
{
versionKey: false,
});
module.exports = mongoose.model('Profile', profileSchema);
here is process page
import React, { useState } from "react";
import axios from "axios";
import { LogIn } from "../redux/action";
import { connect } from "react-redux";
import ImageUpload from "./PhotoUpload";
import { useNavigate } from "react-router-dom";
import { FromProcess, FromProcessContainer, ButtonCreate } from "./CreatingElements";
function CreatingAccount (props) {
const navigate = useNavigate();
const [state,setState] = useState({
currentStep: 1,
name: "",
DoB: "",
activity: "",
topics: "",
drinks: "",
about: ""
});
const handleChange = e => {
const { name, value } = e.target;
setState(state => ({
...state, // <-- copy previous state
[name]: value, // <-- update property
}));
};
const handleSubmit = async e => {
e.preventDefault();
const { user } = props;
let { name, DoB, activity, topics, drinks, about } = state;
await axios.post("/users/profile", {
name,
DoB,
activity,
topics,
drinks,
about,
id: user.id
});
const profileId = {
person: user.id,
name,
DoB,
activity,
about,
topics,
drinks
};
props.LogIn(user.id, user.nickname, profileId);
navigate("/listUsers");
};
const _next = () => {
let currentStep = state.currentStep;
currentStep = currentStep >= 2 ? 3 : currentStep + 1;
setState(state => ({
...state, // <-- copy previous state
currentStep: currentStep// <-- update property
}));
};
const _prev = () => {
let currentStep = state.currentStep;
currentStep = currentStep <= 1 ? 1 : currentStep - 1;
setState(state => ({
...state, // <-- copy previous state
currentStep: currentStep// <-- update property
}));
};
function previousButton() {
let currentStep = state.currentStep;
if (currentStep !== 1) {
return (
<>
<ButtonCreate
style={{ color: "#3103ff" }}
className="btn"
type="button"
onClick={_prev}
>
Previous
</ButtonCreate>
<br />
</>
);
}
return null;
}
function nextButton() {
let currentStep = state.currentStep;
if (currentStep < 3) {
return (
<ButtonCreate
className="btn"
type="button"
onClick={_next}
data-cy="next-process"
style={{
marginBottom: "25px",
color: "#FFF",
backgroundColor: "#3103ff"
}}
>
Next
</ButtonCreate>
);
}
return null;
}
return (
<>
<FromProcessContainer>
<FromProcess onSubmit={handleSubmit} >
<p>Step {state.currentStep}</p>
<br/>
<Step1
currentStep={state.currentStep}
handleChange={handleChange}
name={state.name}
DoB={state.DoB}
activity={state.activity}
/>
<Step2
currentStep={state.currentStep}
handleChange={handleChange}
topics={state.topics}
drinks={state.drinks}
/>
<Step3
currentStep={state.currentStep}
handleChange={handleChange}
about={state.about}
/>
{previousButton()}
{nextButton()}
</FromProcess>
</FromProcessContainer>
</>
);
}
function Step1(props) {
if (props.currentStep !== 1) {
return null;
}
return (
<div className="form-group">
<label>
<input
value={props.name}
onChange={props.handleChange}
type="text"
name="name"
placeholder="Your name"
required
data-cy="input-name-process"
/>
</label>
<label>
<input
value={props.DoB}
onChange={props.handleChange}
type="date"
name="DoB"
placeholder="Date of Birth"
max="2010-01-01"
min="1930-12-31"
required
data-cy="input-Dob-process"
/>
</label>
<label>
<input
value={props.activity}
onChange={props.handleChange}
type="text"
name="activity"
required
placeholder="Place of work or study (required)"
data-cy="input-activity-process"
/>
</label>
</div>
);
}
function Step2(props) {
if (props.currentStep !== 2) {
return null;
}
return (
<div className="form-group">
<label>
<input
value={props.topics}
onChange={props.handleChange}
type="text"
name="topics"
placeholder="Favorite topics: (Optional)"
/>
</label>
<label>
<input
type="text"
value={props.drinks}
onChange={props.handleChange}
name="drinks"
placeholder="Favorite drink: (Optional)"
/>
</label>
</div>
);
}
function Step3(props) {
if (props.currentStep !== 3) {
return null;
}
return (
<>
<ImageUpload/>
<div className="form-group">
<label>
<input
value={props.about}
onChange={props.handleChange}
className="form-control"
type="text"
name="about"
placeholder="Caption (Optional)"
/>
</label>
</div>
<button
type="submit"
className="btn"
data-cy="submit-process"
style={{
backgroundColor: "#3103ff",
marginBottom: "25px",
color: "#FFF"
}}
>
Save it
</button>
</>
);
}
const mapStateToProps = state => ({
user: state.user
});
const mapDispatchToProps = dispatch => ({
LogIn: (id, nickname, profileId) => dispatch(LogIn(id, nickname, profileId))
});
export default connect(mapStateToProps, mapDispatchToProps)(CreatingAccount);
backend for /process
router.post('/profile', async (req, res) => {
const {
name,
DoB,
activity,
topics,
about,
drinks,
avatar,
id,
} = req.body;
const user = await Person.findOne({ _id: id }).exec();
if (!user.profileId) {
const newProfile = await Profile.create({
person: id,
name,
DoB,
activity,
topics,
about,
drinks,
avatar,
});
await Person.updateOne(user, { $set: { profileId: newProfile._id } });
return res.send({
success: true,
});
}
await Person.updateOne({ _id: user.profileId }, {
$set: {
activity,
topics,
about,
drinks,
avatar,
},
});
});
In your axios post, add a .then and a .catch, console.log() the result of the post whether its a success or a failure, also, not sure if u have the favourite variable defined globally but that might also be a source of the error
Issue
In the case of backend errors the API is returning a valid response that is missing a profileId property.
router.post('/profileEdit', async (req, res) => {
const { id } = req.body;
const response = await Profile.findOne({ person: id });
if (response) {
res.send({ success: true, profileId: response });
} else {
res.send({ success: false, err: 'Something went wrong' }); // <-- here, no profileId
}
});
This doesn't cause axios to return a Promise rejection and the frontend code assumes the response object has certain properties.
The error object returned is { success: false, err: 'Something went wrong' } but the code is attempting to access into an undefined profileId property.
axios
.post("/users/profileEdit", { id })
.then(({ data }) => {
setPlace(data.profileId.place); // <-- here, data.profileId undefined
setAge(data.profileId.age);
setFavorite(data.profileId.favorite);
setCaption(data.profileId.cation);
profileInit(data.profileId);
});
data.profileId is undefined, and alone isn't an issue, it's later when trying to access the place property of an undefined object that an error is thrown.
Solution
You can keep the backend code and do a check in the frontend for a successful POST request.
axios
.post("/users/profileEdit", { id })
.then(({ data }) => {
if (data.success) {
// POST success
setPlace(data.profileId.place);
setAge(data.profileId.age);
setFavorite(data.profileId.favorite);
setCaption(data.profileId.cation);
profileInit(data.profileId);
} else {
// POST failure, check error
// access data.err
}
});
It would be better to return a non-200 status code so axios can throw/handle a rejected Promise. You can read more about HTTP status codes here. Below I'm returning a super general Status 500 error, but you will want to be a specific as you need to be, i.e. a 404 if the specific person object isn't found.
Example:
router.post('/profileEdit', async (req, res) => {
const { id } = req.body;
const response = await Profile.findOne({ person: id });
if (response) {
res.send({ profileId: response });
} else {
res.status(500).send({ err: 'Something went wrong' });
}
});
...
axios
.post("/users/profileEdit", { id })
.then(({ data }) => {
// 200 responses
const {
profileId,
profileId: { age, cation, favorite, place },
} = data;
setPlace(place);
setAge(age);
setFavorite(favorite);
setCaption(cation);
profileInit(profileId);
})
.catch(error => {
// non-200 responses
});
this is my front end code, I think the problem is here.
So, when the form is submitted everything that is supposed to work
works but the email shows up empty. Also I was getting an error
in the console here it is...
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
So, i added that useEffect clean up function although im not sure if i did
it right the error is gone. Are these two things related? Somehow the state is not being updated in time. If i hard code the message the email sends out fine. Thank you. Also any additional advice would be cool.
import React, { useEffect, useState } from 'react';
import axios from 'axios';
export default function ContactMe(props) {
const [data, setData] = useState({email: '',phone: '',
message: '', sent: false});
const [thankYouMessage, setThankYouMessage] = useState('');
const goBack = () => {
props.setIsOnHomePage(true);
}
const onChange = (e) => {
const {name, value} = e.target
setData({
...data,
[name]: value
})
}
const resetForm = () => {
setData({
email: '',
phone: '',
message: '',
sent: false,
});
}
// here I think is the problem in this function?
const onSubmit = (e) => {
setData({
...data,
})
axios.post('/api/sendmail', data)
.then(res => {
if(res.data.result !=='success') {
setData({
...data,
sent: false,
})
resetForm();
} else {
setData({
...data,
sent: true,
})
resetForm();
}
}).catch( (err) => {
setData({
...data,
})
})
setThankYouMessage('thank you');
}
useEffect(() => {
const source = axios.CancelToken.source()
return () => {
source.cancel()
}
}, [])
return (
<div className = 'contact-me-form'>
<button className = 'go-back' onClick =
{goBack}>go back</button>
<h1 className = 'contact-me-title'>Contact
me</h1>
<form>
<p className = 'input-title'>email</p>
<input className = 'input-field' defaultValue
= {data.email} onChange={onChange}></input>
<p className = 'input-title' >phone
number</p>
<input className = 'input-field' defaultValue
= {data.phone} onChange={onChange}></input>
<p className = 'input-title' >message</p>
<textarea className = 'text-field'
defaultValue = {data.message} onChange=
{onChange} rows = '3'></textarea>
</form>
<button onClick = {() => {onSubmit()}}
className = 'submit-contact-form-
button'>submit</button>
<p className = 'thank-you-message'>
{thankYouMessage}</p>
</div>
)
}
You need three main changes:
Set name attribute in input/textarea tags;
In onChange handler, get name attribute with e.target.getAttribute("name");
In onSubmit handler, remove setData, because is unecessary;
import React, { useEffect, useState } from "react";
import axios from "axios";
export default function ContactMe(props) {
const [data, setData] = useState({
email: "",
phone: "",
message: "",
sent: false,
});
const [thankYouMessage, setThankYouMessage] = useState("");
const goBack = () => {
props.setIsOnHomePage(true);
};
const onChange = (e) => {
const value = e.target.value;
const name = e.target.getAttribute("name");
setData({
...data,
[name]: value,
});
};
const resetForm = () => {
setData({
email: "",
phone: "",
message: "",
sent: false,
});
};
// here I think is the problem in this function?
const onSubmit = (e) => {
axios
.post("/api/sendmail", data)
.then((res) => {
if (res.data.result !== "success") {
setData({
...data,
sent: false,
});
resetForm();
} else {
setData({
...data,
sent: true,
});
resetForm();
}
})
.catch((err) => {
setData({
...data,
});
});
setThankYouMessage("thank you");
};
useEffect(() => {
const source = axios.CancelToken.source();
return () => {
source.cancel();
};
}, []);
return (
<div className="contact-me-form">
<button className="go-back" onClick={goBack}>
go back
</button>
<h1 className="contact-me-title">Contact me</h1>
<form>
<p className="input-title">email</p>
<input
className="input-field"
defaultValue={data.email}
name="email"
onChange={onChange}
></input>
<p className="input-title">phone number</p>
<input
className="input-field"
defaultValue={data.phone}
name="phone"
onChange={onChange}
></input>
<p className="input-title">message</p>
<textarea
className="text-field"
defaultValue={data.message}
name="message"
onChange={onChange}
rows="3"
></textarea>
</form>
<button
onClick={() => {
onSubmit();
}}
className="submit-contact-form-button">
submit
</button>
<p className="thank-you-message">{thankYouMessage}</p>
</div>
);
}
Please I would be happy if anyone would help me
I have a problem, I can not use the server-side functions, I call the functions with axios, and execute it in react hooks.
I actually build chat, which is why I use react hook, because I want messages to be updated all the time.
I also use firestore. There I save the messages, and receive them through the server side function.
It's a component of the chat - it's causing me problems, I do not understand why.
The server side functions work great, I tested them in postman, and they worked. The problem is that I can't run them in a function component. I do not know what I'm doing wrong.
The error I get here is in the line chat.users.length> 0?, When I make this comparison I get that chat.users is undefined, but I do not understand why because I initialize it at first, using a server side function , Which gives the necessary information
I'm very confused, and I'm new here on the site, I'm trying to figure out why it has not worked for two whole days
I think I might be confused by syntax, for example using an unnecessary dispatch inside component of the chat
i got this error:
enter image description here
component of the chat
import React, { useEffect, useState } from 'react';
import './style.css';
import { useDispatch, useSelector } from 'react-redux';
import { getRealtimeUsers, updateMessage, getRealtimeConversations } from '../../redux/actions/chatActions';
import { Fragment } from 'react';
const User = (props) => {
const { chat, onClick } = props;
return (
<div onClick={() => onClick(chat)} className="displayName">
<div className="displayPic">
<img src="https://i.pinimg.com/originals/be/ac/96/beac96b8e13d2198fd4bb1d5ef56cdcf.jpg" alt="" />
</div>
<div style={{ display: 'flex', flex: 1, justifyContent: 'space-between', margin: '0 10px' }}>
<span style={{ fontWeight: 500 }}>{chat.firstName} {chat.lastName}</span>
<span className={chat.isOnline ? `onlineStatus` : `onlineStatus off`}></span>
</div>
</div>
);
}
const HomePage = (props) => {
const dispatch = useDispatch();
const user = useSelector(state => state.user.credentials);
const chat = useSelector(state => state.chat);
const [chatStarted, setChatStarted] = useState(false);
const [chatUser, setChatUser] = useState('');
const [message, setMessage] = useState('');
const [userUid, setUserUid] = useState(null);
let unsubscribe;
useEffect(() => {
//unsubscribe = dispatch(getRealtimeUsers(user.handle))
dispatch(getRealtimeUsers());
}, []);
//console.log(user);
//componentWillUnmount
useEffect(() => {
return () => {
//cleanup
//unsubscribe.then(f => f()).catch(error => console.log(error));
unsubscribe.then(f => f()).catch(error => console.log(error));
}
}, []);
//function
const initChat = (chat) => {
setChatStarted(true)
setChatUser(`${chat.firstName} ${chat.lastName}`)
setUserUid(chat.handle);
console.log(chat);
dispatch(getRealtimeConversations({ uid_1: user.handle, uid_2: chat.handle }));
}
const submitMessage = (e) => {
const msgObj = {
user_uid_1: user.handle,
user_uid_2: userUid,
message
}
if (message !== "") {
dispatch(updateMessage(msgObj))
.then(() => {
setMessage('')
});
}
//console.log(msgObj);
}
return (
<Fragment>
<section className="container">
<div className="listOfUsers">
{console.log(chat)}
{
//chat.users != undefined
chat.users.length > 0 ?
chat.users.map(user => {
return (
<User
onClick={initChat}
key={user.handle}
user={user}
/>
);
})
: null
}
</div>
<div className="chatArea">
<div className="chatHeader">
{
chatStarted ? chatUser : ''
}
</div>
<div className="messageSections">
{
chatStarted ?
chat.conversations.map(con =>
<div style={{ textAlign: con.user_uid_1 == user.handle ? 'right' : 'left' }}>
<p className="messageStyle" >{con.message}</p>
</div>)
: null
}
</div>
{
chatStarted ?
<div className="chatControls">
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Write Message"
/>
<button onClick={submitMessage}>Send</button>
</div> : null
}
</div>
</section>
</Fragment>
);
}
export default HomePage;
This is the axios:
app.get('/realtimeUsers', FBAuth, getRealtimeUsers );
app.post('/updateMessage', FBAuth, updateMessage);
app.get('/realtimeConversations', FBAuth, getRealtimeConversations);
And this is the server side functions - They work 100% - I checked them many times and they worked.:
const { db } = require('../util/admin');
exports.getRealtimeUsers = (req, res) => {
db.collection("users")
.onSnapshot((querySnapshot) => {
const users = [];
querySnapshot.forEach(function (doc) {
if (doc.data().handle != req.user.handle) {
users.push(doc.data());
}
});
return res.json(users);
});
}
exports.updateMessage = (req, res) => {
db.collection('conversations')
.add({
...req.body,
isView: false,
createdAt: new Date()
})
.then(() => {
return res.json({ message: "Conversations added successfully" });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
}
exports.getRealtimeConversations = (req, res) => {
console.log(JSON.stringify("testing"));
console.log(JSON.stringify(req.query));
console.log(JSON.parse(req.query.user));
console.log(JSON.parse(req.query.user).uid_1);
console.log(JSON.parse(req.query.user).uid_2);
db.collection('conversations')
.where('user_uid_1', 'in', [JSON.parse(req.query.user).uid_1, JSON.parse(req.query.user).uid_2])
.orderBy('createdAt', 'asc')
.onSnapshot((querySnapshot) => {
const conversations = [];
querySnapshot.forEach(doc => {
console.log(JSON.stringify(doc));
if (
(doc.data().user_uid_1 == JSON.parse(req.query.user).uid_1 && doc.data().user_uid_2 == JSON.parse(req.query.user).uid_2)
||
(doc.data().user_uid_1 == JSON.parse(req.query.user).uid_2 && doc.data().user_uid_2 == JSON.parse(req.query.user).uid_1)
) {
conversations.push(doc.data())
}
});
console.log(conversations);
return res.json(conversations);
})
//return res.json([]);
}
this is the actions that used in the client side, here i call to the axios:
import { userConstants } from "../types";
import axios from 'axios';
export const getRealtimeUsers = () => (dispatch) => {
dispatch({ type: `${userConstants.GET_REALTIME_USERS}_REQUEST` });
axios
.get('/realtimeUsers')
.then((res) => {
console.log(res);
dispatch({
type: `${userConstants.GET_REALTIME_USERS}_SUCCESS`,
payload: res.data
});
})
.catch((err) => console.log(err))
}
export const updateMessage = (msgObj) => (dispatch) => {
axios.post('/updateMessage', msgObj)
.then(() => { })
.catch((err) => console.log(err));
}
export const getRealtimeConversations = (user) => (dispatch) => {
//user = { uid_1: "from visualcode", uid_2: "userUid" };
console.log(JSON.stringify(user));
axios.get('/realtimeConversations',
{
params: {
user: JSON.stringify(user)
//uid_1:JSON.stringify("user.handle"),
//uid_2:JSON.stringify("userUid")
}
}
)
.then((res) => {
dispatch({
type: userConstants.GET_REALTIME_MESSAGES,
payload: res.data
});
})
.catch((err) => console.log(err))
}
I am not able to understand your whole code flow, i.e., how the chat.users will be populated before initChat is called.
But still, for your problem, you should always put a check for undefined values while iterating through an array.
<div className="listOfUsers">
{console.log(chat)}
{
//chat.users != undefined
chat && chat.users && chat.users.length > 0 &&
chat.users.map(user => {
return (
<User
onClick={initChat}
key={user.handle}
user={user}
/>
);
})
}
</div>
I am building an app, sort of dictionary app. In my app a user can add, update and delete a word. Something happened and now my update function does not work. I don't understand why. When I click an update button an error appears in the console. Your hints and suggestions are appreciated.
Word update API
// #route Put api/words/:id
// #desc Update a word by id
// #access Private
router.put(
'/:id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateWordInput(req.body);
// Check validation
if (!isValid) {
// Return any errors
return res.status(400).json(errors);
}
Profile.findOne({ user: req.user.id }).then(profile => {
Word.findById(req.params.id).then(word => {
// Check for word owner
if (word.user.toString() !== req.user.id) {
return res.status(401).json({ notauthorized: 'User not authorized' });
}
const wordID = req.params.id;
const wordInput = req.body;
// Update
Word.findByIdAndUpdate(
{ _id: wordID },
{ $set: wordInput },
{ returnOriginal: false },
(err, word) => {
if (err) {
console.log(err);
}
}
)
.then(word => {
res.json(word);
})
.catch(err =>
res
.status(404)
.json({ wordcannotbeupdated: 'word cannot be updated' })
);
});
});
}
);
Word actions
export const updateWord = (id, updatedWord, history) => dispatch => {
axios
.put(`/api/words/${id}`, updatedWord)
.then(res => {
dispatch({
type: UPDATE_WORD,
payload: res.data
});
history.push('/my-words');
})
.catch(err =>
dispatch({
type: UPDATE_WORD,
payload: ''
})
);
};
Word reducer
import {
GET_WORDS_BY_USER,
ADD_WORD,
UPDATE_WORD,
GET_WORD_BY_ID,
DELETE_WORD,
SEARCH_WORD
} from '../actions/types';
const initialState = {
words: [],
word: {},
loading: false
};
export default function(state = initialState, action) {
switch (action.type) {
case ADD_WORD:
return {
...state,
words: [action.payload, ...state.words]
};
case GET_WORDS_BY_USER:
return {
...state,
words: action.payload
};
case GET_WORD_BY_ID:
return {
...state,
word: action.payload
};
case UPDATE_WORD:
return {
...state,
words: action.payload
};
case SEARCH_WORD:
return {
...state,
words: action.payload
};
case DELETE_WORD:
return {
...state,
words: state.words.filter(word => word._id !== action.payload)
};
default:
return state;
}
}
Edit Word React component
import React, { Component } from 'react';
import { Link, withRouter } from 'react-router-dom';
import TextFieldGroup from '../common/TextFieldGroup';
import SelectListGroup from '../common/SelectListGroup';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { getWordByID, updateWord } from '../../actions/wordActions';
import isEmpty from '../../validation/is-empty';
import {
lexisOptions,
grammarOptions,
partOfSpeechOptions,
styleOptions,
originOptions,
sphereOptions
} from './options';
class EditWord extends Component {
constructor(props) {
super(props);
this.state = {
ugrWordCyr: '',
rusTranslation: '',
example: '',
exampleTranslation: '',
origin: '',
sphere: '',
see: '',
lexis: '',
grammar: '',
partOfSpeech: '',
style: '',
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.onCheck = this.onCheck.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({ errors: nextProps.errors });
}
if (nextProps.words.word[0]) {
nextProps.words.word.map(word => {
word.ugrWordCyr = !isEmpty(word.ugrWordCyr) ? word.ugrWordCyr : '';
word.rusTranslation = !isEmpty(word.rusTranslation)
? word.rusTranslation
: '';
word.rusTranslation = !isEmpty(word.rusTranslation)
? word.rusTranslation
: '';
word.example = !isEmpty(word.example) ? word.example : '';
word.exampleTranslation = !isEmpty(word.exampleTranslation)
? word.exampleTranslation
: '';
word.origin = !isEmpty(word.origin) ? word.origin : '';
word.sphere = !isEmpty(word.sphere) ? word.sphere : '';
word.see = !isEmpty(word.see) ? word.see : {};
word.lexis = !isEmpty(word.lexis) ? word.lexis : {};
word.grammar = !isEmpty(word.grammar) ? word.grammar : {};
word.see = !isEmpty(word.see) ? word.see : {};
word.style = !isEmpty(word.style) ? word.style : {};
this.setState({
ugrWordCyr: word.ugrWordCyr,
rusTranslation: word.rusTranslation,
example: word.example,
exampleTranslation: word.exampleTranslation,
origin: word.origin,
sphere: word.sphere,
style: word.style,
lexis: word.lexis,
grammar: word.grammar,
see: word.see,
partOfSpeech: word.partOfSpeech
});
});
}
}
componentDidMount() {
this.props.getWordByID(this.props.match.params.id);
}
onSubmit(e) {
e.preventDefault();
const wordData = {
ugrWordCyr: this.state.ugrWordCyr,
rusTranslation: this.state.rusTranslation,
example: this.state.example,
exampleTranslation: this.state.exampleTranslation,
origin: this.state.origin,
sphere: this.state.sphere,
see: this.state.see,
lexis: this.state.lexis,
grammar: this.state.grammar,
partOfSpeech: this.state.partOfSpeech,
style: this.state.style
};
let id = this.props.match.params.id;
// Change a function to update
this.props.updateWord(id, wordData, this.props.history);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onCheck(e) {
this.setState({
current: !this.state.current
});
}
render() {
const { errors } = this.state;
return (
<div className="add-word">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<Link to="/my-words" className="btn btn-light">
Go Back
</Link>
<h1 className="display-4 text-center">Edit Word</h1>
<form onSubmit={this.onSubmit}>
<TextFieldGroup
placeholder="Бала"
info="Введите слово на уйгурском"
name="ugrWordCyr"
value={this.state.ugrWordCyr}
onChange={this.onChange}
error={errors.ugrWordCyr}
/>
<TextFieldGroup
placeholder="Ребенок"
info="Введите слово на русском"
name="rusTranslation"
value={this.state.rusTranslation}
onChange={this.onChange}
error={errors.rusTranslation}
/>
<div className="form-check mb-form">
<input
type="checkbox"
className="form-check-input"
name="see"
value={this.state.see}
onChange={this.onCheck}
id="see"
/>
<label htmlFor="see">Смотри</label>
</div>
<TextFieldGroup
placeholder=""
info="Введите пример предложения на уйгурском"
name="example"
value={this.state.example}
onChange={this.onChange}
error={errors.example}
/>
<TextFieldGroup
placeholder=""
info="Введите перевод примерного предложения на русском"
name="exampleTranslation"
value={this.state.exampleTranslation}
onChange={this.onChange}
error={errors.exampleTranslation}
/>
<h6>Происхождение слова</h6>
<SelectListGroup
placeholder="Арабское"
name="origin"
value={this.state.origin}
onChange={this.onChange}
error={errors.origin}
options={originOptions}
/>
<h6>Сфера употребления слова</h6>
<SelectListGroup
placeholder="Физика"
name="sphere"
value={this.state.sphere}
onChange={this.onChange}
error={errors.sphere}
options={sphereOptions}
/>
<h6>Лексика слова</h6>
<SelectListGroup
placeholder="лексика"
name="lexis"
value={this.state.lexis}
onChange={this.onChange}
error={errors.lexis}
options={lexisOptions}
/>
<h6>Стиль слова</h6>
<SelectListGroup
placeholder="стиль"
name="style"
value={this.state.style}
onChange={this.onChange}
error={errors.style}
options={styleOptions}
/>
<h6>Часть речи</h6>
<SelectListGroup
placeholder=""
name="partOfSpeech"
value={this.state.partOfSpeech}
onChange={this.onChange}
error={errors.partOfSpeech}
options={partOfSpeechOptions}
/>
<h6>Грамматика слова</h6>
<SelectListGroup
placeholder="грамматика"
name="grammar"
value={this.state.grammar}
onChange={this.onChange}
error={errors.grammar}
options={grammarOptions}
/>
<input
type="submit"
value="submit"
className="btn btn-info btn-block mt-4"
/>
</form>
</div>
</div>
</div>
</div>
);
}
}
EditWord.propTypes = {
getWordByID: PropTypes.func.isRequired,
errors: PropTypes.object.isRequired,
updateWord: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
errors: state.errors,
words: state.words
});
export default connect(
mapStateToProps,
{ getWordByID, updateWord }
)(withRouter(EditWord));
axios
.put(/api/words/${id}, updatedWord)
if you are using relative api endpoint then it will be going to request to
<react-server-host:port(which is 3000 in your case)>/api/words/${id}
and i am assuming you are running your backend service on another port than 3000 . Thats why it is giving error 404 not found
So when you are requesting api, write absolute endpoint
<backend-server-host:port>/api/words/${id}
I'm using a react and nodejs as backend, backend work properly I guess when I tried in postman but when trying to connect into my frontend it didnt insert the properly
here's the code in react app
class CreateChapter extends React.Component {
constructor(props){
super(props);
this.state={
// editorState: EditorState.createWithContent(convertFromRaw(JSON.parse(content))),
titleChapter: "",
editorState: EditorState.createEmpty(),
id: this.props.location.state.id,
status: '',
errorSaving: '',
saved: false
}
}
// handle editor change
onEditorStateChange = (editorState) => {
this.setState({ editorState })
}
//handle title change
onTitleChange = (event) => {
this.setState({
titleChapter: event.target.value
})
}
//load data chapter saved
loadChapterSaved = (data) => {
this.setState({
titleChapter: data.titleChapter,
editorState: data.editorState,
status: 'saved'
})
}
//load data chapter published
loadChapterPublished = (data) => {
this.setState({
titleChapter: data.titleChapter,
editorState: data.editorState,
status: 'published'
})
}
//save data
onSaveData = () => {
const convertedData = convertToRaw(this.state.editorState.getCurrentContent());
fetch('http://localhost:3001/new-chapter',{
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
id: this.state.id,
editorState: convertedData,
titleChapter: this.state.titleChapter,
status: this.state.status
})
})
.then(response => response.json())
.then( chapter => {
console.log(chapter)
if(chapter === 'empty input'){
this.setState({
errorSaving: 'Please fill Title Chapter and the Content'
})
}else{
this.loadChapterSaved(chapter);
}
})
}
render() {
const { classes, ...rest } = this.props;
const {editorState, id, errorSaving, saved} = this.state;
console.log('status', this.state.status)
return (
<div>
<HeaderHome/>
<div className={classNames(classes.main, classes.mainRaised)}>
<div className={classes.container}>
<div className={classes.storymargin}>
<h2 className={classes.title}>New Chapter</h2>
<Card className={classes.chaptercontainer}>
<CardContent>
<TextField
fullWidth
label=" New Chapter Title"
id="chapter-title"
onChange={this.onTitleChange}
InputProps={{
disableUnderline: true,
classes:{
input: classes.titleinput
}
}}
InputLabelProps={{
shrink: true,
className: classes.titleformlabel
}}
margin="normal"
/>
<Editor
editorState={editorState}
wrapperClassName={classes.homewrapper}
editorClassName={classes.homeeditor}
toolbarClassName={classes.toolbar}
placeholder="Begin typing..."
onEditorStateChange={this.onEditorStateChange}
/>
</CardContent>
{errorSaving ?
<div className={classes.errorSavingChap}>
{errorSaving}
</div>
:
""
}
<CardActions className={classes.displaybutton}>
<Button size="small" clor="primary" className={classes.buttonnextchap}
onClick={this.onSaveData}>
Save
</Button>
<Button size="small" clor="primary" className={classes.buttonnextchap}
onClick={this.onPublishData}>
Publish
</Button>
</CardActions>
</Card>
</div>
</div>
</div>
<div className={classes.footer}>
<Footer />
</div>
</div>
);
} }
export default withStyles(createStoryStyle)(CreateChapter);
the problem is this.state.status didnt send to the backend properly, it always receives initial state which is null. I think it has something to do with how setState is asynchronous, but I'm not quite sure. When I tried it in postman, it does work.
here's my backend
app.post('/new-chapter', (req, res) => {
const { id, titleChapter, editorState, status } = req.body;
if(titleChapter === '' || editorState === ''){
db('story').where('id_user', '=', id)
.increment('chapter', 0)
.returning('chapter')
.then( chapter => {
res.json('empty input')
})
.catch( err => res.json('cannot add chapter'))
}else{
db('story').where('id_user', '=', id)
.increment('chapter', 1)
.returning('chapter')
.then( chapter => {
db.select('entry').from('story').where('id_user', '=',id)
.then( newChap => {
const chapterEntry = newChap[newChap.length - 1].entry;
db('chapter').where('id_user', '=', id)
.returning('*')
.insert({
chapter: chapter[0],
titlechapter: titleChapter,
content: editorState,
id_user: id,
status: status,
entry: chapterEntry,
})
.then( newChapter => {
res.json(newChapter[0])
})
.catch( err => res.json('cannot add new chapter'))
}
)
})
.catch( err => res.json('cannot add new chapter'))
}
})