I'm using functional components and within that components I'm using useState so when I redirect to that component and log some text so it printed almost thousand times and this is very frustrating for me please help.
import React, { useState, useEffect } from 'react';
import Modal from 'react-bootstrap/Modal'
import Form from 'react-bootstrap/Form'
import Button from 'react-bootstrap/Button'
import DatePicker from 'react-date-picker';
import axios from 'axios';
import useToken from '../../../account/useToken';
import TokenInfo from '../../../account/TokenInfo'
function CreateOrUpdateEvent(props) {
const { token, setToken } = useToken();
const [value, onChange] = useState(new Date());
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const {userId} = TokenInfo();
console.log("Add Event Rendered");
if(props.id != undefined)
{
const headers = {
'Authorization': `bearer ${token}`
}
axios.get(`https://localhost:5001/Event/GetById/${props.id}`,
{
headers: headers
})
.then(function (res) {
setEventName(res.data.name);
setEventDescription(res.data.description);
setEventDate(res.data.eventDate);
})
.catch(function (error) {
console.log(error);
});
}
const setEventName = (name) => {
setName(name);
}
const setEventDescription = (description) => {
setDescription(description);
}
const setEventDate = (eventDate) => {
var eventDate = new Date(eventDate);
onChange(eventDate);
}
const saveEvent = () => {
var event = {
Id: props.id,
Name: name,
Description: description,
EventDate: value,
IsDeleted: false,
CreatedDateTime: new Date(),
UserId: userId
}
const headers = {
'Authorization': `bearer ${token}`
}
axios.post('https://localhost:5001/Event/CreateOrUpdate', event, {
headers: headers
})
.then(function () {
props.onHide();
})
.catch(function (error) {
console.log(error);
});
}
return (
<>
<Modal
{...props}>
<Modal.Header>
<Modal.Title>Create Event</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form.Group controlId="formBasicName">
<Form.Label>Name</Form.Label>
<Form.Control type="text" placeholder="Name" value={props.id != undefined ? name : ''} onChange={e => setName(e.target.value)} />
</Form.Group>
<Form.Group controlId="formBasicDescription">
<Form.Label>Description</Form.Label>
<Form.Control as="textarea" rows={3} value={props.id != undefined ? description : ''} onChange={e => setDescription(e.target.value)} />
</Form.Group>
<Form.Group controlId="formBasicDate">
<Form.Label>Date</Form.Label>
<div>
<DatePicker
onChange={onChange}
value={props.id != undefined ? value : new Date()}
/>
</div>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={props.onHide}>
Close
</Button>
<Button variant="primary" onClick={saveEvent}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
)
};
export default React.memo(CreateOrUpdateEvent);
and I already remove StrictMode tag from index.js so please don't suggest that because it's not work for me.
The problem occurs, because every time you update a state, your axios function will be executed, updating the state again, causing an infinite loop.
Try to use the code below. Let me know if it made the trick.
useEffect(() => {
const headers = {
'Authorization': `bearer ${token}`
}
axios.get(`https://localhost:5001/Event/GetById/${props.id}`,
{
headers: headers
})
.then(function (res) {
setEventName(res.data.name);
setEventDescription(res.data.description);
setEventDate(res.data.eventDate);
})
.catch(function (error) {
console.log(error);
});
}, [props.id])
It is posible that your error is related to the axios query to https://localhost:5001/Event/GetById/${props.id}.
Remember that setting a new value into your state makes a new render, so every time the query ends, it'll be called again.
You should put that query into a useEffect to just make it when the props.id changes:
function CreateOrUpdateEvent(props) {
const { token, setToken } = useToken();
const [value, onChange] = useState(new Date());
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const {userId} = TokenInfo();
useEffect(() => {
console.log("Add Event Rendered");
if(props.id != undefined) {
const headers = {
'Authorization': `bearer ${token}`
}
axios.get(`https://localhost:5001/Event/GetById/${props.id}`,
{
headers: headers
})
.then(function (res) {
setEventName(res.data.name);
setEventDescription(res.data.description);
setEventDate(res.data.eventDate);
})
.catch(function (error) {
console.log(error);
});
}
}, [props.id]);
...
Related
I'm new to React, and I'm trying to make a recpie app with react, right know I want to save the data in json file from the add form. so I can save the data but when I want to redirect the user to the home page using useEffict with navigate. I can't go to the create page when adding navigate to the useEffict.
Create file code:
import { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useFetch } from "../../hooks/useFetch";
// Styles
import "./Create.css";
export default function Create() {
const [title, setTitle] = useState("");
const [method, setMethod] = useState("");
const [cookingTime, setCookingTime] = useState("");
const [newIngredient, setNewIngredient] = useState("");
const [ingredients, setIngredients] = useState([]);
const { postData, data } = useFetch("http://localhost:3000/recipes", "POST");
const ingredientsInput = useRef(null);
const navigate = useNavigate();
// Methods
const handleSubmit = (e) => {
e.preventDefault();
postData({
title,
ingredients,
method,
cookingTime: cookingTime + " minutes",
});
};
const handleAdd = (e) => {
e.preventDefault();
const ing = newIngredient.trim();
if (ing && !ingredients.includes(ing)) {
setIngredients((preIng) => [...preIng, ing]);
}
setNewIngredient("");
ingredientsInput.current.focus();
};
useEffect(() => {
if (data) {
navigate("/");
console.log(data);
}
}, [data, navigate]);
return (
<div className="create">
<form onSubmit={handleSubmit}>
<label>
<span>Recipe Title:</span>
<input
type="text"
onChange={(e) => setTitle(e.target.value)}
value={title}
required
/>
</label>
<label>
<span>Recipe ingredients:</span>
<div className="ingredients">
<input
type="text"
onChange={(e) => setNewIngredient(e.target.value)}
value={newIngredient}
ref={ingredientsInput}
/>
<button onClick={handleAdd} className="btn">
Add
</button>
</div>
</label>
{ingredients.length > -1 && (
<p>
Current ingredients:{" "}
{ingredients.map((ing) => (
<span key={ing}>{ing}, </span>
))}
</p>
)}
<label>
<span>Recipe Method:</span>
<textarea
onChange={(e) => setMethod(e.target.value)}
value={method}
required
/>
</label>
<label>
<span>Recipe Time (minutes):</span>
<input
type="number"
onChange={(e) => setCookingTime(e.target.value)}
value={cookingTime}
required
/>
</label>
<button className="btn">Submit</button>
</form>
</div>
);
}
useFetch file code:
import { useState, useEffect } from "react";
export const useFetch = (url, method = "GET") => {
const [data, setData] = useState(null);
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState(null);
const [option, setOption] = useState(null);
const postData = (data) => {
setOption({
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
};
useEffect(() => {
const controller = new AbortController();
const fetchData = async (fetchOption) => {
setIsPending(true);
try {
const res = await fetch(url, {
...fetchOption,
signal: controller.signal,
});
if (!res.ok) {
throw new Error(res.statusText);
}
const data = await res.json();
setIsPending(false);
setData(data);
setError(null);
} catch (err) {
if (err.name === "AbortError") {
console.log("the fetch was aborted");
} else {
setIsPending(false);
setError("Could not fetch the data");
}
}
};
if (method === "GET") {
fetchData();
}
if (method === "POST") {
fetchData(option);
}
return () => {
controller.abort();
};
}, [url, option, method]);
return { data, isPending, error, postData };
};
I don't know from where the issue came.
The problem was from useFetch file. when I want to do a post request I shoud cheack if the option useState has a value.
Before I was just check if there is a post method:
const [option, setOptions] = useState(null);
if (method === "POST") {
fetchData(option);
}
Know I'm checking if there is a value in option
const [option, setOptions] = useState(null);
if (method === "POST" && option) {
fetchData(option);
}
You basically trying to add a variable that is not a react state variable into the useEffect on update
const [recipes, setReceipies] = useState();
useEffect(async ()=> { const {data} = awawit useFetch("http://localhost:3000/recipes", "POST")
setReceipies(data);
},[])
navigate("/");
},[recipes]);
Or ofc you can navigate all the way from the mounting useEffect
Good Luck
after you save the data, simply add this code
const history = createBrowserHistory()
history.push(`/`)
I have big apps, that use history, and I never had a problem with it.
and I recomend you to use SWR for data-fetching - React Hooks for Data Fetching.
very simple and powerfull tool:
https://swr.vercel.app/
I have a page users.jsx (parent) and a component DialogEditUser.jsx (child) and i would like to pass a specific data of a user that is located in parent to child by it's id (using find method)
This passed data should be loaded to its input in react modal as a value.
users.jsx Code:
import React, { useState, useEffect } from 'react'
import DialogAddUser from 'src/components/DialogAddUser'
import { getUsers} from 'src/Service/api'
const Typography = () => {
const [users, setUsers] = useState([])
useEffect(() => {
getAllUsers()
}, [])
const deleteUserData = async (id) => {
setConfirmDialog({
...setConfirmDialog,
isOpen: false,
})
await deleteUser(id)
getAllUsers()
setNotify({
isOpen: true,
message: 'Article Deleted Successfully.',
type: 'error',
})
}
const getAllUsers = async () => {
let response = await getUsers()
setUsers(response.data)
console.log(response.data)
}
return ( //... )
DialogEditUsers.jsx Code:
import { useEffect, useState } from 'react'
import { getUsers, editUser } from '../Service/api'
const initialValue = {
id: '',
code: '',
article: '',
price: '',
vat: '',
status: '',
company_id: '',
}
export default function DialogAddUser() {
const [user, setUser] = useState(initialValue)
const { code, article, price, vat, status, company_id } = user
const normalize = (v) => ({
code: v.code,
article: v.article,
price: Number(v.price),
vat: Number(v.vat),
status: Number(v.status),
company_id: Number(v.company_id),
})
useEffect(() => {
loadUserDetails()
}, [])
const loadUserDetails = async () => {
const response = await getUsers(id)
console.log('loading user details ', response)
setUser(response.data.find((x) => x.id == id))
}
const editUserDetails = async () => {
const response = await editUser(id, normalize(user))
console.log('Edit user details ', response)
}
const onValueChange = (e) => {
console.log(e.target.value)
setUser({ ...user, [e.target.name]: e.target.value })
}
return (
<>
<CModal
visible={visible}
onClose={() => setVisible(false)}
backdrop={'static'}
keyboard={false}
portal={false}
>
<CModalHeader>
<CModalTitle>Edit Article:</CModalTitle>
</CModalHeader>
<CModalBody>
<CForm>
<CFormInput
type="text"
id="exampleFormControlInput1"
label="Code :"
placeholder="Enter Code"
text=" "
aria-describedby="exampleFormControlInputHelpInline"
onChange={(e) => onValueChange(e)}
value={code}
name="code"
/>
<CFormInput
type="text"
id="exampleFormControlInput2"
label="Article :"
placeholder="Enter Article"
text=" "
aria-describedby="exampleFormControlInputHelpInline"
onChange={(e) => onValueChange(e)}
value={article}
name="article"
/>
//...the rest of inputs...
api.js Code:
import axios from 'axios'
const baseURL = 'https://api.factarni.tn/article'
const token =
'eyJhbGciOiJSUzI1NiIsImtpZCI6IjIxZTZjMGM2YjRlMzA5NTI0N2MwNjgwMDAwZTFiNDMxODIzODZkNTAiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiZmFraHJpIGtyYWllbSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BSXRidm1uMS12dWJJcHNxTURKMkNTcDhVcTlmU3I1LUI1T3Y3RHY2SFRNMT1zMTMzNyIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9mYWN0YXJuaSIsImF1ZCI6ImZhY3Rhcm5pIiwiYXV0aF90aW1lIjoxNjYzNzY3ODk5LCJ1c2VyX2lkIjoiaWhqM0JWM0hIRFhpVnUwdmpzV3ZidjMyRDdMMiIsInN1YiI6ImloajNCVjNISERYaVZ1MHZqc1d2YnYzMkQ3TDIiLCJpYXQiOjE2NjM3Njc4OTksImV4cCI6MTY2Mzc3MTQ5OSwiZW1haWwiOiJmYWtocmlpLmtyYWllbUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjEwODU1MTA3MjAwODIwNjMxMjI0NCJdLCJlbWFpbCI6WyJmYWtocmlpLmtyYWllbUBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJnb29nbGUuY29tIn19.bvRTxHfPtJrQjF2BjXqhs7ji738kma55LMFVRb8jkeraWP-JRBi-LRPa0d7OR_-BPwCGuRBXIb6980_PP8wjhBeDdB5B77GujiGn3nUvpPOFeIaM0L7muw1NKo4YCtS3v6ifuywypTbL3_5x3SBFZEH-QV0sp5DAzaA-P3Fn8AwP66o3cUPHGengGpZNsfkJ0FYcqzH-xpyKVVWV'
//i dont mind sharing this token, it's for you to test this code if you need.
const config = { headers: { Authorization: `Bearer ${token}` } }
export const getUsers = async (id) => {
id = id || ''
try {
return await axios.get(`${baseURL}`, config)
} catch (error) {
console.log('Error while calling getArticles api ', error)
}
}
export const editUser = async (id, user) => {
return await axios.put(`${baseURL}/${id}`, user, config)
}
The only node error i'm getting in terminal using this code above (because i dont know how to pass the proper id of specified user) is:
src\components\DialogEditUser.jsx
Line 45:37: 'id' is not defined no-undef
Line 47:47: 'id' is not defined no-undef
Line 51:37: 'id' is not defined no-undef
For better explanation the problem (i dont know how to use online snippets sorry):
So what i'm expecting is: When i click on Edit button, i should get a modal with form that are filled with user data (code, article, price, vat, status and company_id) in each input of the form as value, just like this gif below:
Also, console.log(response.data) in users page shows this:
few days back i also faced the same issue. Solution for me is to create state in parent component and pass state to child. Example for it-
Parent Class
const parent= ()=>{
const [name, setName]= useState('')
const [password, setPassword]= useState('')
return(
<Child setName={setName} setPassword={setPassword} />
)
}
Child Class
const Child = ({setPassword,setName})=>{
return(
<div>
<input type="text" placeholder="Enter Name" onChange={(e)=>setPassword(e.target.value)} />
<input type="text" placeholder="Enter Name" onChange={(e)=>setPassword(e.target.value)} />
</div>
)
}
Hope my answer will help you to solve your problem, if you still facing issue, lemme know i will help you.
In users.jsx, pass props of (user.id):
<DialogEditArticle props={user.id} />
Then, in DialogEditArticle.jsx, create a new data and call in it props:
const DialogEditArticle = (data) => {
console.log(data.props)
Now console.dev, you will get all the ids of user in database (because button edit is inside map function).
Result:
I am trying to fetch data from my Express api which is working, but there is issue in the frontend,
it seems like when I change the input state there is a delay even if I call the functions the fetch data after updating the input state.
Here is my component:
import React, { useState, useEffect } from 'react';
import UsersList from './UsersList.js';
function SearchBox() {
const [input, setInput] = useState("");
const [githubUserResult, setGithubUserResult] = useState([]);
const [gitlabUserResult, setGitlabUserResult] = useState([]);
const [isLoaded, setIsloaded] = useState(false);
const [error, setError] = useState(null);
const handleInputChange = (e) => {
setInput(e.target.value);
}
const searchUser = async (e) => {
e.preventDefault();
searchUserOnGithub(input);
searchUserOnGitLab(input);
setIsloaded(true);
}
const searchUserOnGithub = async (username) => {
await fetch(`/api/github/userinfo/${username}`, {
method: "GET", headers: {
"Content-Type": "application/json",
}
})
.then(res => res.json())
.then(
(result) => {
setGithubUserResult(result);
console.log(githubUserResult);
},
(error) => {
setError(error)
})
}
const searchUserOnGitLab = async (username) => {
await fetch(`/api/gitlab/userinfo/${username}`, {
method: "GET", headers: {
"Content-Type": "application/json",
}
})
.then(res => res.json())
.then(
(result) => {
setGitlabUserResult(result);
console.log(gitlabUserResult);
},
(error) => {
setError(error)
})
}
if (error) {
return <div>Error: {error.message}</div>;
}return (
<div className='search-container'>
<form>
<input type="text" onChange={handleInputChange} />
<button type="button" onClick={searchUser} >Search</button>
</form>
<h3>github</h3><br />
{/*isLoaded ? <UsersList users={githubUserResult} />: ''*/}
<h3>gitLab</h3><br />
{/*isLoaded ? <UsersList users={gitlabUserResult} /> : ''*/}
</div>
)
}
export default SearchBox;
On the console we can see the first attempt failling(first click), and the second one working:
You are logging your states, and your states will not update until the the next render. When you click on the button the following code is executed:
setGithubUserResult(result);
console.log(githubUserResult);
See Closures.
result is the value returned from your request, but githubUserResult has the initial value you declared for the state, which is an empty array. Your state (githubUserResult) does not update synchronously and immediately when you set the state. Hence the stale value of the state.
If you want to access the updated value, one way would be to use the result not the state directly.
I am using React formik I created Create form and update form in a single component. The update based on the One id if update means on page the id will be send in post method and get an data Database and the data show in the input field and update also working fine.But in the rendering component are slow and cause lot of errors and I want useEffect will be outside render function and the field working a slight delay after typing to display
Any Idea and any better Method
Thank you for Help
import React, { useEffect, useState } from "react";
import { Formik, Field, Form, FastField } from "formik";
import axios from "axios";
import {
Button,
TextField,
Grid,
makeStyles,
MenuItem,
FormControl,
Select,
InputLabel,
Checkbox,
FormControlLabel
} from "#material-ui/core/";
const baseurl = "http://localhost:8080/api/v1/";
const useStyles = makeStyles((theme) => ({
}));
function App() {
const classes = useStyles();
var PropertyId = sessionStorage.getItem("Propertyid");
const data = { propertyId: PropertyId };
const isAddMode = !PropertyId;
const initialValues = {
propertyName: "",
displayName: "",
description: "",
propertyType: ""
};
function onSubmit(fields, { setStatus }) {
setStatus();
if (isAddMode) {
createProperty(fields);
} else {
updateProperty(fields);
}
}
function createProperty(fields) {
axios
.post(baseurl + "propertybasicpropertydetails", fields, {
headers: {
"Access-Control-Allow-Origin": "*"
}
})
.then((res) => {
sessionStorage.setItem("Propertyid", JSON.stringify(res.data));
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
}
function updateProperty(fields) {
var updateData = { ...data, ...fields };
axios
.put(baseurl + "propertybasicpropertydetailsupdate", updateData, {
headers: {
"Access-Control-Allow-Origin": "*"
}
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
}
return (
<div className={classes.paper}>
<Formik initialValues={initialValues} onSubmit={onSubmit}>
{({ setFieldValue }) => {
// eslint-disable-next-line
const [property, setProperty] = useState({});
// eslint-disable-next-line
// eslint-disable-next-line
useEffect(() => {
if (!isAddMode) {
const path = baseurl + "propertybasicpropertydetailsvalue";
axios
.post(path, data, {
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json"
}
})
.then((res) => {
console.log(res.data);
const fields = [
"propertyName",
];
fields.forEach((field) =>
setFieldValue(field, res.data[field], false)
);
setProperty(res.data);
});
}
}, [setFieldValue]);
return (
<Form>
<Grid container spacing={4}>
<Grid item xs={12} sm={6}>
<Field
name="propertyName"
type="text"
as={TextField}
fullWidth
label="Property Name"
autoFocus
variant="outlined"
size="small"
/>
</Grid>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.submit}
>
Save and Continue
</Button>
</Grid>
</Form>
);
}}
</Formik>
</div>
);
}
export { App };
How would I show a server response error or success message the cleanest way ?
Right now, I'm using an async function to make an axios request, and on success/error im just updating a local state (with react-hook-form), but I feel like it's "ugly" and I want the pages to be as clean as possible and put the code to handle success and error messages in the service request, behind the scenes.
Example :
ForgotPassword.jsx
import React, { useState } from 'react';
import Layout from '../components/core/Layout';
import axios from 'axios';
import { useForm } from 'react-hook-form';
import { Button, Form, Alert } from 'react-bootstrap';
import { regex } from '../constants';
import { isAuth } from '../helpers';
import { forgotPassword } from '../services/User';
import { Redirect } from 'react-router-dom';
const Forgot = () => {
const {
handleSubmit,
register,
errors,
getValues,
setError,
setValue,
clearError
} = useForm({
mode: 'onBlur'
});
register({ name: 'responseError' });
register({ name: 'responseSuccess' });
const { responseSuccess } = getValues();
const onSubmit = async values => {
try {
const response = await forgotPassword(values);
setValue([{ responseSuccess: response.data.message }]);
// set response success msg to local state responseSuccess
} catch (error) {
setError('responseError', '', error);
// set response error msg to local state responseError
}
};
const forgotPasswordForm = () => (
<>
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Group>
<Form.Label>Email address</Form.Label>
<Form.Control
name='email'
ref={register({
required: true,
pattern: {
value: regex.email,
message: 'Invalid email address'
}
})}
type='email'
placeholder='Enter email'
isInvalid={errors.email}
/>
<Form.Control.Feedback type={errors.email ? 'invalid' : 'valid'}>
{errors.email && errors.email.message}
</Form.Control.Feedback>
</Form.Group>
<Button variant='primary' type='submit'>
Submit
</Button>
</Form>
<br />
{errors.responseError && (
<Alert
variant='danger'
dismissible
onClose={() => clearError('responseError')}>
{errors.responseError.message}
</Alert>
)}
</>
);
const forgotPasswordFormSuccess = () => (
<Alert
variant='success'
className='mt-5'
dismissible
onClose={() => setValue([{ responseSuccess: '' }])}>
{responseSuccess}
</Alert>
);
if (isAuth()) return <Redirect to='/' />;
return (
<Layout>
<div>
<h1>Forgot password</h1>
{responseSuccess ? forgotPasswordFormSuccess() : forgotPasswordForm()}
</div>
</Layout>
);
};
export default Forgot;
forgotPassword Function
export const forgotPassword = async ({ email }) => {
return new Promise(async (resolve, reject) => {
try {
const response = await Axios({
method: 'PUT',
url: `${process.env.REACT_APP_API}/forgot-password`,
data: { email }
});
resolve(response);
} catch (error) {
if (error.response) {
reject(error.response && error.response.data.error);
}
reject('Something went wrong. please try again later.');
}
});
};
Hope this is what you want
export const forgotPassword = ({ email }) => {
return new Promise((resolve, reject) => {
axios(`${process.env.REACT_APP_API}/forgot-password`, {
method: 'PUT',
data: { email }
})
.then(res => resolve(res.data))
.catch(err => reject(err))
});
};
const onSubmit = values => {
forgotPassword(values)
.then(res => setValue([{ responseSuccess: res.message }]))
.catch(err => setError('responseError', '', err));
};