I have two components. My dashbord-component, in which I am dispatching the getAllDropDownRessort, which triggers the findAll function in my dropdownRessortSlice and an edit-component, in which I am dispatching the getDropdownRessort, which triggers the findOne function in the same slice.
When I calling the edit-component, it not ends rendering, and the redux-devtools tell me that it always renders the findAll function.
Thanks for your help.
Here is my dashbord-component:
const DropdownRessorts = () => {
//bring in
const {dropdownRessort, isLoading, isError, message} = useSelector((state)=>state.dropdownRessort);
const dispatch = useDispatch();
useEffect(()=>{
if(isError){
console.log(message);
}
dispatch(getAllDropdownRessort());
return ()=>{
dispatch(reset());
}
}, [dispatch, isError, message]);
//bring out
const [formdata, setFormdata] = useState(
{
lis:[],
videos:[
{
iframe:"",
ressort:"",
timestamp:"",
theme:"",
title:""
},
{
iframe:"",
ressort:"",
timestamp:"",
theme:"",
title:""
},
{
iframe:"",
ressort:"",
timestamp:"",
theme:"",
title:""
}
],
themen:[],
}
);
const {lis, videos,themen} = formdata;
const handleChange = (e)=>{
setFormdata((prevState)=>({
...prevState,
[e.target.name]: e.target.value,
}))
}
const onSubmit = (e)=>{
e.preventDefault();
const dropdownRessortData = {
lis,
videos,
themen,
}
dispatch(createDropdownRessort(dropdownRessortData));
}
if(isLoading){
return <Spinner/>
}
return (
<Container>
<CrudTitleHolder>
<CrudTitle>Dropdown Menü Ressort</CrudTitle>
</CrudTitleHolder>
<DataformDropDownRessort onSubmit={onSubmit}>
{dropdownRessort.length > 0 && <DataHolder>
{dropdownRessort.map((item)=>(
<div key={item._id}>
{console.log(item._id)}
<h4 id="name">{item.name}</h4>
<div id="listenpunkte">Menüpunkte: {item.lis.join(", ")}<span style={{color:"var(--red)"}}> Bitte nur nach Absprache verändern.</span></div>
<VideoWrapper>
<DataVideoholder>
<iframe src={item.videos[0].iframe} title={item.videos[0].title}/>
<h4>{item.videos[0].ressort}</h4>
<h4>{item.videos[0].theme}</h4>
<p>{item.videos[0].title}</p>
</DataVideoholder>
<DataVideoholder>
<iframe src={item.videos[1].iframe} title={item.videos[0].title}/>
<h4>{item.videos[1].ressort}</h4>
<h4>{item.videos[1].theme}</h4>
<p>{item.videos[1].title}</p>
</DataVideoholder>
<DataVideoholder>
<iframe src={item.videos[2].iframe} title={item.videos[0].title}/>
<h4>{item.videos[2].ressort}</h4>
<h4>{item.videos[2].theme}</h4>
<p>{item.videos[2].title}</p>
</DataVideoholder>
</VideoWrapper>
<Themen>Themen: {item.themen.join(", ")}</Themen>
<DataButtonHolder>
<Link to={`/dropdownressortedit/${item._id}`}className="link">
<DataUpdateButton>Update</DataUpdateButton>
</Link>
<DataDeleteButton onClick={()=>dispatch(deleteDropdownRessort(item._id))}>Löschen</DataDeleteButton>
</DataButtonHolder>
</div>
))}
</DataHolder>}
<section className="menupoints" encType="multipart/form-data">
<DataLabel htmlFor="lis_ressorts">Menüpunkte Ressorts</DataLabel>
<DataInput type="text" name="lis_ressorts" id="lis_ressorts" value={lis} onChange={handleChange}/>
</section>
<div className="videos">
<section className="video_1">
<DataLabel htmlFor="ressortvideo_1_iframe">Video 1</DataLabel>
<DataInput type="file" name="ressortvideo_1_iframe" id="video_1_iframe"
style={{background:"var(--blue)", color:"var(--white)"}}
value={videos[0].iframe} onChange={handleChange}/>
<DataLabel htmlFor="video_1_ressort">Video 1 Ressortzuordnung</DataLabel>
<DataInput type="text" name="video_1_ressort" id="video_1_ressort" value={videos[0].ressort} onChange={handleChange}/>
<DataLabel htmlFor="video_1_theme">Video 1 Themenzuordnung</DataLabel>
<DataInput type="text" name="video_1_theme" id="video_1_theme" value={videos[0].theme} onChange={handleChange}/>
<DataLabel htmlFor="video_1_title">Video 1 Titelzuordnung</DataLabel>
<DataInput type="text" name="video_1_title" id="video_1_title" value={videos[0].title} onChange={handleChange}/>
</section>
<section className="video_2">
<DataLabel htmlFor="ressortvideo_2_iframe">Video 2</DataLabel>
<DataInput type="file" name="ressortvideo_2_iframe" id="video_2_iframe"
style={{background:"var(--blue)", color:"var(--white)"}}
value={videos[1].iframe} onChange={handleChange}/>
<DataLabel htmlFor="video_2_ressort">Video 2 Ressortzuordnung</DataLabel>
<DataInput type="text" name="video_1_ressort" id="video_2_ressort" value={videos[1].ressort} onChange={handleChange}/>
<DataLabel htmlFor="video_2_theme">Video 2 Themenzuordnung</DataLabel>
<DataInput type="text" name="video_2_theme" id="video_2_theme" value={videos[1].theme} onChange={handleChange}/>
<DataLabel htmlFor="video_2_title">Video 2 Titelzuordnung</DataLabel>
<DataInput type="text" name="video_2_title" id="video_2_title" value={videos[1].title} onChange={handleChange}/>
</section>
<section className="video_3">
<DataLabel htmlFor="ressortvideo_3_iframe">Video 3</DataLabel>
<DataInput type="file" name="ressortvideo_3_iframe" id="video_3_iframe"
style={{background:"var(--blue)", color:"var(--white)"}}
value={videos[2].iframe} onChange={handleChange}/>
<DataLabel htmlFor="video_3_ressort">Video 3 Ressortzuordnung</DataLabel>
<DataInput type="text" name="video_3_ressort" id="video_3_ressort" value={videos[2].ressort} onChange={handleChange}/>
<DataLabel htmlFor="video_3_theme">Video 3 Themenzuordnung</DataLabel>
<DataInput type="text" name="video_3_theme" id="video_3_theme" value={videos[2].theme} onChange={handleChange}/>
<DataLabel htmlFor="video_3_title">Video 3 Titelzuordnung</DataLabel>
<DataInput type="text" name="video_3_title" id="video_3_title" value={videos[2].title} onChange={handleChange}/>
</section>
</div>
<section className="themen">
<DataLabel htmlFor="themen_ressorts">Themen</DataLabel>
<DataInput type="text" name="themen_ressorts" id="themen_ressorts" value={themen} onChange={handleChange}/>
</section>
<DataButtonHolder>
<DataSendButton type="submit">Absenden</DataSendButton>
</DataButtonHolder>
</DataformDropDownRessort>
</Container>
)
}
export default DropdownRessorts
Here is my edit-component:
const DropdownRessortEdit = () => {
const dispatch = useDispatch();
const {dropdownRessort, isLoading, isError, message} = useSelector((state)=>state.dropdownRessort);
const { id } = useParams();
const [data, setData] = useState({
lis: [],
videos:[
{
iframe:"",
ressort:"",
theme:"",
title:"",
},
{
iframe:"",
ressort:"",
theme:"",
title:"",
},
{
iframe:"",
ressort:"",
theme:"",
title:"",
},
],
themen:[],
})
const {lis, videos, themen} = data;
useEffect(()=>{
if(isError){
window.alert(message);
}
dispatch(getDropdownRessort(id));
return ()=>{
dispatch(reset());
}
}, [dispatch, isError, message, id]);
const updateData = (value)=>{
return setData((prev) =>{
return { ...prev, ...value}
})
}
const onSubmit = (e)=>{
e.preventDefault();
const updateDropdownRessortData = {
lis,
videos,
themen,
}
dispatch(updateDropdownRessort(updateDropdownRessortData));
}
if(isLoading){
return <Spinner/>
}
return (
<Container>
<Navbar/>
<TitleHolder>
<Title>Update DropdownRessort</Title>
</TitleHolder>
<ContentHolder>
<UpdateForm onSubmit={onSubmit} encType="multipart/form-data">
{dropdownRessort.length > 0 && <DataHolder>
{dropdownRessort.map((item)=>(
<div key={item._id}>
<Label htmlFor="name">Name</Label>
<Input type="text" name="name" id="name" value={item.name} onChange={(e)=>updateData({name: e.target.value})}/>
<Label htmlFor="lis">MenüPunkte</Label>
<Input type="text" name="lis" id=""lis value={item.lis.join(", ")} onChange={(e)=>updateData({lis: e.target.value})}/>
<VideoWrapper>
<VideoSection>
<Label htmlFor="ressortvideo_1_iframe">Video 1 update</Label>
<Input type="text" name="ressortvideo_1_iframe" id="iframe" value={item.videos[0].iframe} onChange={(e)=>updateData({iframe: e.target.value})}/>
<Label htmlFor="ressort">Ressort</Label>
<Input type="text" name="ressort" id="ressort" value={item.videos[0].ressort} onChange={(e)=>updateData({ressort: e.target.value})}/>
<Label htmlFor="theme">Thema</Label>
<Input type="text" name="theme" id="theme" value={item.videos[0].theme} onChange={(e)=>updateData({theme: e.target.value})}/>
<Label htmlFor="title">title</Label>
<Input type="text" name="title" id="title" value={item.videos[0].title} onChange={(e)=>updateData({title: e.target.value})}/>
</VideoSection>
<VideoSection>
<Label htmlFor="ressortvideo_2_iframe">Video 2 update</Label>
<Input type="text" name="ressortvideo_2_iframe" id="iframe" value={data.videos[1].iframe} onChange={(e)=>updateData({iframe: e.target.value})}></Input>
<Label htmlFor="ressort">Ressort</Label>
<Input type="text" name="ressort" id="ressort" value={item.videos[1].ressort} onChange={(e)=>updateData({ressort: e.target.value})}/>
<Label htmlFor="theme">Thema</Label>
<Input type="text" name="theme" id="theme" value={item.videos[1].theme} onChange={(e)=>updateData({theme: e.target.value})}/>
<Label htmlFor="title">title</Label>
<Input type="text" name="title" id="title" value={item.videos[1].title} onChange={(e)=>updateData({title: e.target.value})}/>
</VideoSection>
<VideoSection>
<Label htmlFor="ressortvideo_3_iframe">Video 3 update</Label>
<Input type="text" name="ressortvideo_3_iframe" id="iframe" value={item.videos[2].iframe} onChange={(e)=>updateData({iframe: e.target.value})}></Input>
<Label htmlFor="ressort">Ressort</Label>
<Input type="text" name="ressort" id="ressort" value={item.videos[2].ressort} onChange={(e)=>updateData({ressort: e.target.value})}/>
<Label htmlFor="theme">Thema</Label>
<Input type="text" name="theme" id="theme" value={item.videos[2].theme} onChange={(e)=>updateData({theme: e.target.value})}/>
<Label htmlFor="title">title</Label>
<Input type="text" name="title" id="title" value={item.videos[2].title} onChange={(e)=>updateData({title: e.target.value})}/>
</VideoSection>
</VideoWrapper>
<Label htmlFor="themen">Themen</Label>
<Input type="text" name="themen" id="themen" value={item.themen} onChange={(e)=>updateData({themen: e.target.value})}/>
<ButtonHolder>
<UpdateButton type="submit">Update</UpdateButton>
</ButtonHolder>
</div>
))}
</DataHolder>
}
</UpdateForm>
</ContentHolder>
<Footer/>
</Container>
)
}
export default DropdownRessortEdit
Try to remove dispatch from the dependencies in useEffect hook.
Because you want to fetch dispatch only when the component mount, not each time dispatch is changed.
So, in your code case, it will change dispatch which will re-trigger the hook which will fetch dispatch again which will change dispatch causes the hook to re-trigger and so on .. causing Infinity Loop.
You can try to put dispatch in a React.useCallback function as shown in this example w3school React.useCallback
Related
I am having an issue where my change function makes my checkboxes go from either true/false to just be 'on'. All the fields in my form use onInputChange and they all work accept for the checkboxes. So if I type in the text fields it updates properly and saves to my database with no issue but if I click a checkbox no matter the value that it started as it will change to 'on' and gives a cannot convert varchar to int error. I am looking for a new function to update the checkboxes properly and keep the onInputChange one for the text fields.
I did create one like below and it seems to change from true to false properly but 100% clears the rest of the data already in the form.
const onCheckboxChange = e => {
setUser({
active = !active });
console.log(user)
};
import React, { useState, useEffect } from "react";
import axios from "axios";
import { useParams, Link } from "react-router-dom";
const UpdateUser = () => {
const { collectorID } = useParams();
const [user, setUser] = useState({});
const {collectorOptionsID, programBucketID,financeCompanyID, active, firstName, middleInitial, lastName, collectorCode, aging1to15, aging31to45, aging31to60, agingOver60, programBucketA, programBucketB, programBucketC, programBucketSU, financeCompany, debtType } = user;
const onInputChange = e => {
setUser({
...user, [e.target.name]: e.target.value });
console.log(user)
};
useEffect(() => {
loadUser();
}, []);// eslint-disable-line react-hooks/exhaustive-deps
const loadUser = async () => {
const result = await axios.get(`https://support.pawneeleasing.com/PublishedCollectorAPI/api/Collector/${collectorID}`);
setUser(result.data);
console.log(result.data);
};
const onSubmit = async e => {
e.preventDefault();
console.log(active);
await axios.put(process.env.NODE_ENV === 'development' ? `${process.env.REACT_APP_DEV_UPDATE}${collectorID}` : `${process.env.REACT_APP_PRO_UPDATE}${collectorID}`, {
// await axios.put(`https://support.pawneeleasing.com/PublishedCollectorAPI/api/Collector/${collectorID}`, {
collectorID: collectorID,
collectorOptionsID: collectorOptionsID,
programBucketID: programBucketID,
financeCompanyID: financeCompanyID,
active: active,
firstName: firstName,
middleInitial: middleInitial,
lastName: lastName,
collectorCode: collectorCode,
debtType: debtType,
aging1to15: aging1to15,
aging31to45: aging31to45,
aging31to60:aging31to60,
agingOver60: agingOver60,
programBucketA: programBucketA,
programBucketB: programBucketB,
programBucketC: programBucketC,
programBucketSU: programBucketSU,
financeCompany: financeCompany,
});
};
return (
<div className="newUser">
<h1>Update Collector</h1>
<form className="newUserForm" >
<div className="newUserItem">
{/*active or inactive User*/}
<label>active</label>
<div className="newUserCheckboxContainer">
<input
type='checkbox'
name="active"
defaultChecked={active}
onClick={e => onInputChange(e)}
/>
</div>
{/*Collector First Name*/}
<label>First Name</label>
<input
type="text"
name="firstName"
placeholder="First Name"
defaultValue={firstName}
onChange={e => onInputChange(e)}
/>
{/*Collector Middle Initial*/}
<label>Middle Initial</label>
<input
type="text"
name="middleInitial"
placeholder="Middle Initial"
defaultValue={middleInitial}
onChange={e => onInputChange(e)}
/>
{/*Collector Last Name*/}
<label>Last Name</label>
<input
type="text"
name="lastName"
placeholder="Last Name"
defaultValue={lastName}
onChange={e => onInputChange(e)}
/>
{/*Collector Code First Initial Middle Initial Last Initial*/}
<label>Collector Code</label>
<input
type="text"
name="collectorCode"
placeholder="Collector Code"
defaultValue={collectorCode}
onChange={e => onInputChange(e)}
/>
{/*Aging Bucket selection section */}
<label>Aging Bucket</label>
<div className='newUserCheckboxContainer'>
<label className='newUserCheckboxLabel'>1-15<br/>
<input
type='checkbox'
className='AgingBucketCheckbox'
defaultValue={aging1to15}
defaultChecked={aging1to15}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>31-45<br/>
<input
type='checkbox'
className='AgingBucketCheckbox'
defaultValue={aging31to45}
defaultChecked={aging31to45}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>31-60<br/>
<input
type='checkbox'
className='AgingBucketCheckboxsm'
defaultValue={aging31to60}
defaultChecked={aging31to60}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>Over 60<br/>
<input
type='checkbox'
className='AgingBucketCheckboxlg'
defaultValue={agingOver60}
defaultChecked={agingOver60}
onChange={e => onInputChange(e)}
/></label>
</div>
{/*Progam code selection section*/}
<label>Program Bucket</label>
<div className='newUserCheckboxContainer'>
<label className='newUserCheckboxLabel'>A<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketA}
defaultChecked={programBucketA}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>B<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketB}
defaultChecked={programBucketB}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>C<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketC}
defaultChecked={programBucketC}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>SU<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketSU}
defaultChecked={programBucketSU}
onChange={e => onInputChange(e)}
/></label>
</div>
{/*Finance Company selection section*/}
<label>Finance Company</label>
<div className='newUserCheckboxContainer'>
<input
type="text"
name="financeCompany"
placeholder="financeCompany"
defaultValue={financeCompany}
onChange={e => onInputChange(e)}
/>
</div>
<label>Debt Type</label>
<div className='newUserCheckboxContainer'>
<input
type="text"
name="debtType"
placeholder="debtType"
defaultValue={debtType}
onChange={e => onInputChange(e)}
/>
</div>
<button type='submit' onClick={(event)=>onSubmit(event)} className="userListAddButton">Update Collector</button>
<Link className="userListGoBackButton" to='/users'>Go Back</Link>
</div>
</form>
</div>
);
}
export default UpdateUser;
You need to copy the rest of user properties by rest operator
const onCheckboxChange = e => {
setUser(prev => ({
...prev,
active: !prev.active
}))
};
I can't find the solution to this error in my code every time I try to type something in my input field. TypeError: Cannot read property 'name' of undefined
but I've done like the answer in the following post https://stackoverflow.com/a/57519847/16568705 still not work
Here is all code
MultiStepForm.js
import { useForm, useStep } from "react-hooks-helper";
import React from "react";
import { LaporanPolisi } from "./stepForm/LaporanPolisi";
const defaultData = {
laporanPolisi: {
nomorLp: "",
jenisKelamin: 0,
tanggalLp: "",
kerugian: 0,
uraianSingkat: "",
pasalDilanggar: [
{
undangUndang: "",
pasal: ""
}
]
},
pelapor: [],
saksi: [],
korban: [],
terlapor: [],
barangBukti: [],
tkp: {
kodeProvinsi: "",
kodeKabupaten: "",
kodeKecamatan: "",
kodeKelurahan: "",
kodeRT: "",
kodeRW: ""
}
}
const steps = [
{ id: "laporanPolisi" },
{ id: "pelapor" },
{ id: "saksi" },
{ id: "korban" },
{ id: "terlapor" },
{ id: "barangBukti" },
{ id: "tkp" },
{ id: "submit" }
]
export const MultiStepForm = () => {
const [formData, setForm] = useForm(defaultData);
const { step, navigation } = useStep({
steps,
initialStep: 0,
});
const props = { formData, setForm, navigation };
switch (step.id) {
case "laporanPolisi":
return <LaporanPolisi {...props} />;
case "pelapor":
return "Pelapor";
case "saksi":
return "Saksi";
case "korban":
return "Korban";
case "terlapor":
return "Terlapor";
case "barangBukti":
return "Barang Bukti";
case "tkp":
return "TKP";
case "submit":
return "Submit";
default:
return <LaporanPolisi {...props} />;
}
};
LaporanPolisi.js
import React from 'react';
export const LaporanPolisi = ({ formData, setForm, navigation }) => {
const {
laporanPolisi
} = formData;
const handleChange = e => {
const { name, value } = e.target;
console.log(name)
setForm(prevState => ({
...prevState,
laporanPolisi: {
...prevState.laporanPolisi,
[name]: value
}
}));
};
return (
<div className="card">
<div className="card-header">
<h4>Laporan Polisi</h4>
</div>
<div className="card-body">
<div className="row">
<div className="col-md-6">
<div className="form-group">
<label htmlFor="nomorLp">Nomor Laporan</label>
<input onChange={handleChange} id="nomorLp"
placeholder="Nomor Laporan" type="text" className="form-control" name="nomorLp" value={laporanPolisi.nomorLp} />
</div>
</div>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="jenisKelamin">Jenis Kelamin</label>
<select onChange={handleChange} value={laporanPolisi.jenisKelamin} id="jenisKelamin" name="jenisKelamin" className="custom-select">
<option value={0}>Laki-Laki</option>
<option value={1}>Perempuan</option>
</select>
</div>
</div>
{/* <input placeholder="Tanggal Laporan" type="text" className="form-control" name="nomorLp" value={jenisKelamin}></input>
<input placeholder="Kerugian" type="text" className="form-control" name="nomorLp" value={nomorLp}></input>
<input placeholder="Uraian Singkat" type="text" className="form-control" name="nomorLp" value={nomorLp}></input>
<input placeholder="Nomor Laporan" type="text" className="form-control" name="nomorLp" value={nomorLp}></input> */}
</div>
</div>
<pre>
<code>
{JSON.stringify(formData.laporanPolisi)}
</code>
</pre>
</div>
)
}
error Image:
https://imgur.com/a/JC89ykW
thanks before sorry for my bad english.
All react components have to return a component, for example: ...
In your MultiStepForm.js there are only 2 cases meet this requirement.
And this is the way I dealt with input, if you want to update setForm, you can add it in hanldeChange:
export default function BasicExample({ formData, setForm, navigation }) {
const [input1, setInput1] =useState('');
const [input2, setInput2] =useState('');
const handleChange = e => {
e.preventDefault()
console.log(input1)
console.log(input2)
};
return (
<form onSubmit={handleChange}>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="nomorLp">Nomor Laporan</label>
<input onChange={e => setInput1(e.target.value)} id="nomorLp"
placeholder="Nomor Laporan" type="text" className="form-control" name="nomorLp" value={input1} />
</div>
</div>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="jenisKelamin">Jenis Kelamin</label>
<select onChange={e => setInput2(e.target.value)} value={input2} id="jenisKelamin" name="jenisKelamin" className="custom-select">
<option value={0}>Laki-Laki</option>
<option value={1}>Perempuan</option>
</select>
</div>
</div>
<button type="submit"> Summit</button>
</form>
);
}
Resolved
i need to define name like this
https://imgur.com/POGiQ5z
in section Nested Object at https://github.com/revelcw/react-hooks-helper
and then, this is my fullcode after change:
LaporanPolisi.js
import React from 'react';
export const LaporanPolisi = ({ formData, setForm, navigation }) => {
const {
laporanPolisi
} = formData;
const handleSubmit = e => {
e.preventDefault();
};
return (
<div className="card">
<div className="card-header">
<h4>Laporan Polisi</h4>
</div>
<div className="card-body">
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col-md-6">
<div className="form-group">
<label htmlFor="nomorLp">Nomor Laporan</label>
<input onChange={setForm} id="nomorLp"
placeholder="Nomor Laporan" type="text" className="form-control" name="laporanPolisi.nomorLp" value={laporanPolisi.nomorLp} />
</div>
</div>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="jenisKelamin">Jenis Kelamin</label>
<select onChange={setForm} id="jenisKelamin" name="laporanPolisi.jenisKelamin" className="custom-select" value={laporanPolisi.jenisKelamin}>
<option value={0}>Laki-Laki</option>
<option value={1}>Perempuan</option>
</select>
</div>
</div>
<div className="col-md-6">
<button className="btn btn-primary" onClick={() => navigation.previous()} type="button">Previous</button>
</div>
<div className="col-md-6">
<button className="btn btn-primary" onClick={() => navigation.next()} type="button">Next</button>
</div>
{/* <input placeholder="Tanggal Laporan" type="text" className="form-control" name="nomorLp" value={jenisKelamin}></input>
<input placeholder="Kerugian" type="text" className="form-control" name="nomorLp" value={nomorLp}></input>
<input placeholder="Uraian Singkat" type="text" className="form-control" name="nomorLp" value={nomorLp}></input>
<input placeholder="Nomor Laporan" type="text" className="form-control" name="nomorLp" value={nomorLp}></input> */}
</div>
</form>
</div>
<pre>
<code>
{JSON.stringify(laporanPolisi)}
</code>
</pre>
</div>
)
}
thanks everyone for answering my question
I am facing this when I am trying to set form error object. Basically, I want to show the errors below each input field. In response, I am getting an array of objects how do I set to my error object?
Error - Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
import axios from "axios";
import React, { useState, useEffect, useCallback } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { register } from "../actions/userActions";
const Register = () => {
const [countriesList, setCountriesList] = useState("");
const [userRegistration, setUserRegistration] = useState({
firstName: "",
lastName: "",
email: "",
password: "",
fullAddress: "",
city: "",
zipCode: "",
country: "",
phone: "",
terms: true,
});
const [userRegistrationError, setUserRegistrationError] = useState({
firstNameError: "",
lastNameError: "",
emailError: "",
passwordError: "",
fullAddressError: "",
cityError: "",
zipCodeError: "",
countryError: "",
phoneError: "",
termsError: "",
});
const dispatch = useDispatch();
const userRegister = useSelector((state) => state.userRegister);
const { loading, errors, success } = userRegister;
useEffect(() => {
const countries = async () => {
try {
const { data } = await axios.get(
`https://restcountries.eu/rest/v2/all`
);
setCountriesList(data);
} catch (err) {
console.error(err);
}
};
countries();
}, []);
useEffect(() => {
const handleErrors = (errors) => {
errors.map((error) => {
if (error.param === "firstname") {
setUserRegistrationError({
...userRegistrationError,
firstNameError: error.msg,
});
}
if (error.param === "email") {
setUserRegistrationError({
...userRegistrationError,
emailError: error.msg,
});
}
return null;
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError]);
const handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setUserRegistration({ ...userRegistration, [name]: value });
};
const handleChkChange = (e) => {
const checked = e.target.checked;
console.log(checked);
setUserRegistration({ ...userRegistration, terms: checked });
};
const handleSubmit = (e) => {
e.preventDefault();
try {
dispatch(register());
} catch (error) {
console.error(error);
}
};
return (
<div className="form_container">
<form action="" onSubmit={handleSubmit}>
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<div className="form-group">
<input
type="text"
name="firstName"
className="form-control"
placeholder="First Name*"
value={userRegistration.firstName}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.firstNameError &&
userRegistrationError.firstNameError}
</p>
</div>
</div>
</div>
<div className="col-6 pr-1">
<div className="form-group">
<input
type="text"
className="form-control"
name="lastName"
placeholder="Last Name*"
value={userRegistration.lastName}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.lastNameError &&
userRegistrationError.lastNameError}
</p>
</div>
</div>
</div>
<hr />
<div className="private box">
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<input
type="email"
className="form-control"
name="email"
id="email_2"
placeholder="Email*"
value={userRegistration.email}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.emailError &&
userRegistrationError.emailError}
</p>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="password"
className="form-control"
name="password"
id="password_in_2"
placeholder="Password*"
value={userRegistration.password}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.passwordError &&
userRegistrationError.passwordError}
</p>
</div>
</div>
<div className="col-12">
<div className="form-group">
<input
type="text"
name="fullAddress"
className="form-control"
placeholder="Full Address*"
value={userRegistration.fullAddress}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.fullAddressError &&
userRegistrationError.fullAddressError}
</p>
</div>
</div>
</div>
{/* /row */}
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="City*"
name="city"
value={userRegistration.city}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.cityError &&
userRegistrationError.cityError}
</p>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Postal Code*"
name="zipCode"
value={userRegistration.zipCode}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.zipCodeError &&
userRegistrationError.zipCodeError}
</p>
</div>
</div>
</div>
{/* /row */}
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<div className="custom-select-form">
<select
className="wide add_bottom_10 form-control"
name="country"
id="country"
value={userRegistration.country}
onChange={handleChange}
>
<option>Country*</option>
{countriesList &&
countriesList.map((country) => (
<option
key={country.alpha2Code}
value={country.alpha2Code}
>
{country.name}
</option>
))}
</select>
<p className="form-vald-error">
{userRegistrationError.countryError &&
userRegistrationError.countryError}
</p>
</div>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Telephone *"
name="phone"
value={userRegistration.phone}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.phoneError &&
userRegistrationError.phoneError}
</p>
</div>
</div>
</div>
{/* /row */}
</div>
<hr />
<div className="form-group">
<label className="container_check">
Accept <Link to="#0">Terms and conditions</Link>
<input
type="checkbox"
name="terms"
checked={userRegistration.terms}
onChange={handleChkChange}
/>
<span className="checkmark" />
<p className="form-vald-error">
{userRegistrationError.termsError &&
userRegistrationError.termsError}
</p>
</label>
</div>
<div className="text-center">
<input
type="submit"
defaultValue="Register"
className="btn_1 full-width"
/>
</div>
</form>
</div>
);
};
export default Register;
your effect depends on userRegistrationError which is an object, reference based. Each time useEffect runs,setUserRegistrationError
creates a new object reference, which leads to an infinite loop since references won't be the same as the previous one.
One approach to avoid this issue and keep the right references, is to pass a callback function to setUserRegistrationError instead than a value. This way userRegistrationError is no longer a dependency, it will be an argument to your function instead:
useEffect(() => {
const handleErrors = (errors) => {
errors.forEach((error) => {
if (error.param === "firstName") {
// here you pass a callback function instead, and userRegistrationError is no longer a dependency
// and returns the next state as expected
setUserRegistrationError(userRegistrationError => ({
...userRegistrationError,
firstNameError: error.msg,
}));
}
if (error.param === "email") {
setUserRegistrationError(userRegistrationError => ({
...userRegistrationError,
emailError: error.msg,
}));
}
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError]);
You have a problem with the second useEffect, the first time you update your state userRegistrationError, the component re-rendered and re-executed the useeffect because the dependency userRegistrationError has changed and the process gets repeated again because the state gets updated every render.
useEffect(() => {
const handleErrors = (errors) => {
errors.map((error) => {
if (error.param === "firstname") {
setUserRegistrationError({
...userRegistrationError,
firstNameError: error.msg,
});
}
if (error.param === "email") {
setUserRegistrationError({
...userRegistrationError,
emailError: error.msg,
});
}
return null;
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError ]); //replace userRegistrationError by setUserRegistrationError
Here is my class component
handleChange =(e) => {
this.setState({
[e.target.id]: e.target.value
})
}
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={this.handleChange}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={this.handleChange}/>
</div>
As you can see we can track all the input with one function component.
But how to get the same result as that inside the functional component?
Now I am setting state for each element.
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={e =>setEmail(e.target.value)}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={e => setPassword(e.target.value)}/>
</div>
The update is similar. Given some state, data for example, then a handler may look like:
const handleChange = (e) => {
const { id, value } = e.target;
setData((data) => ({
...data,
[id]: value
}));
};
Use a functional state update to spread in the existing state and use the input's id and value from the onChange event to update that section of state.
Full code example
function App() {
const [data, setData] = useState({});
const handleChange = (e) => {
const { id, value } = e.target;
setData((data) => ({
...data,
[id]: value
}));
};
return (
<div className="App">
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={handleChange} />
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={handleChange} />
</div>
<div>Email: {data.email}</div>
<div>Password: {data.password}</div>
</div>
);
}
declare one state containing both email and pasword for your functional component as below :
let [loginInfo, setLoginInfo] = useState({email:'',password:''})
handleChange =(e) => {
setLoginInfo({
...loginInfo,
[e.target.id]: e.target.value
})
}
you form
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={handleChange}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={handleChange}/>
</div>
You can create object of form input and assign it to a state like below
const [formInput, setFormInput] = useState({
email: "",
password: ""
});
And set it like this
const handleChange = (e) => {
setFormInput((prevState) => ({
...prevState,
[e.target.id]: e.target.value
}));
};
See this codesandbox code
const [input, setInput] = useState({email:"", password:""})
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" value={input.email}
onChange={e => setInput({...input,[e.target.id]: e.target.value}}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" value={input.password}
onChange={e => setInput({...input,[e.target.id]: e.target.value}}/>
</div>
You can try this code.
In this way you can handle both inputs using same state without handler
I'm having a pretty specific problem in my React with Rails API backend app. I have a Redux form that I am using to create a new object, a real estate listing. The state is changing in my onChange function but when I click submit, the page refreshes and nothing was created. I put debugger inside my handleOnSubmit and it’s not hitting, any ideas?
ListingForm.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { updateListingFormData } from '../actions/listingForm';
import { createListing } from '../actions/listings';
class ListingForm extends Component {
handleOnChange = event => {
const { name, value } = event.target;
const currentListingFormData = Object.assign({}, this.props.listingFormData, {
[name]: value
})
this.props.updateListingFormData(currentListingFormData)
}
handleOnSubmit = (event) => {
debugger;
event.preventDefault()
this.props.createListing(this.props.listingFormData)
}
render() {
const { title, price, location, description, img_url, agent_name, agent_number, agent_email } = this.props.listingFormData;
return (
<div className="ListingForm">
<h3>Add New Listing</h3>
<form onSubmit={(event) => this.handeOnSubmit(event)}>
<label htmlFor="listing_title">Title</label>
<input
type="text"
name="title"
value={title}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing Title"
/>
<label htmlFor="listing_location">Location</label>
<input
type="text"
name="location"
value={location}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing City or County"
/>
<label htmlFor="listing_price">Price</label>
<input
type="integer"
name="price"
value={price}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing Price"
/>
<label htmlFor="listing_description">Description</label>
<input
type="text"
name="description"
value={description}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing Description"
/>
<label htmlFor="listing_image">Attach Image</label>
<input
type="text"
name="img_url"
value={img_url}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing Image"
/>
<label htmlFor="agent_name">Listing Agent</label>
<input
type="text"
name="agent_name"
value={agent_name}
onChange={(event) => this.handleOnChange(event)}
placeholder="Agent Name"
/>
<label htmlFor="agent_number">Listing Agent Phone</label>
<input
type="text"
name="agent_number"
value={agent_number}
onChange={(event) => this.handleOnChange(event)}
placeholder="Agent Phone"
/>
<label htmlFor="agent_email">Agent Email</label>
<input
type="text"
name="agent_email"
value={agent_email}
onChange={(event) => this.handleOnChange(event)}
placeholder="Agent Email"
/>
<button type="submit"> Add Listing</button>
</form>
</div>
)
}
}
const mapStateToProps = state => {
return {
listingFormData: state.listingFormData
}
}
export default connect(mapStateToProps, {updateListingFormData,createListing})(ListingForm);
In your form onSubmit you've misspelt handleOnSubmit (you left out the l)