ReferenceError: Cannot access 'steps' before initialization - reactjs

I'm trying to make wizards in ReactJS. I'm following an online tutorial, however they haven't explained how to make it by multiple pages. So I tried my own way to apply it, but it didn't work.
Here is the code of the first page:
import React, { useState } from "react";
import { Button, Form } from "react-bootstrap";
import { Link } from "react-router-dom";
import "./style.css";
function NewGame() {
const [activeStep, setActiveStep] = useState(steps[0]);
const [steps, setSteps] = useState([
{
key: "firstStep",
label: "My First Step",
isDone: true,
component: firstStep,
},
]);
const index = steps.findIndex((x) => x.key === activeStep.key);
setSteps((prevStep) =>
prevStep.map((x) => {
if (x.key === activeStep.key) x.isDone = true;
return x;
})
);
const firstStep = () => {
return (
<div>
<div className="box">
<div className="steps">
<ul className="nav">
{steps.map((step, i) => {
return (
<li
key={i}
className={`${
activeStep.key === step.key ? "active" : ""
} ${step.isDone ? "done" : ""}`}
>
<div>
Step {i + 1}
<br />
<span>{step.label}</span>
</div>
</li>
);
})}
</ul>
</div>
<div className="step-component">{activeStep.component()}</div>
<div className="btn-component">
<input
type="button"
value="Back"
onClick={handleBack}
disabled={steps[0].key === activeStep.key}
/>
<input
type="button"
value={
steps[steps.length - 1].key !== activeStep.key
? "Next"
: "Submit"
}
onClick={handleNext}
/>
</div>
</div>
<Form className="column justify-content-center page">
<Form.Group className="mb-3" controlId="exampleForm.ControlTextarea1">
<Form.Label>Benzersiz Ad</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
<Form.Group className="mb-3" controlId="exampleForm.ControlTextarea1">
<Form.Label>Görünen İsim</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
<Form.Group className="mb-3" controlId="exampleForm.ControlTextarea1">
<Form.Label>Oyun Açıklaması</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
<Form.Group className="mb-3" controlId="exampleForm.ControlTextarea1">
<Form.Label>Oyun Türü</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
<Form.Group className="mb-3" controlId="exampleForm.ControlTextarea1">
<Form.Label>Bireysel</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
<Form.Group className="mb-3" controlId="exampleForm.ControlTextarea1">
<Form.Label>Oyun Durumu</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
<Form.Group className="mb-3" controlId="exampleForm.ControlTextarea1">
<Form.Label>Açık</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
</Form>
<Link to="/PageTwo">
<Button className="button" variant="outline-secondary">
İ L E R İ
</Button>{" "}
</Link>
</div>
);
const handleNext = () => {
if (steps[steps.length - 1].key === activeStep.key) {
alert("You have completed all steps.");
}
setActiveStep(steps[index + 1]);
};
const handleBack = () => {
const index = steps.findIndex((x) => x.key === activeStep.key);
if (index === 0) return;
setSteps((prevStep) =>
prevStep.map((x) => {
if (x.key === activeStep.key) x.isDone = false;
return x;
})
);
setActiveStep(steps[index - 1]);
};
};
}
export default NewGame;
So when I run this code I have this error in the website:
ReferenceError: Cannot access 'steps' before initialization
NewGame
C:/Users/SAMSUNG/Documents/src/pages/NewGame.js:6
3 | import { Link } from "react-router-dom";
4 | import "./style.css";
5 | function NewGame() {
> 6 | const [activeStep, setActiveStep] = useState(steps[0]);
7 | const [steps, setSteps] = useState([
8 | {
9 | key: "firstStep",
Thank you!

The error is telling you that the variable steps is initialized on line 7, but you're using it on line 6 to set the initial value of the activeStep state variable. You cannot use a variable before it's initialized, hence the message "Cannot access 'steps' before initialization".
Move line 6 after the statement that begins on line 7.

Related

React app hanging when typing on input with onChange state

When I start typing anything in the input fields, the page hangs and nothing gets typed. It was working fine before, it just started happening suddenly. I don't know what went wrong.
I have tried adding onBlur and the problem persists. I tried adding value={registerEmail} in the input fields and the problem still remains as well.
Pleae check :(
import React, {useState} from 'react'
import {
createUserWithEmailAndPassword,
onAuthStateChanged,
signInWithEmailAndPassword,
signOut
} from 'firebase/auth'
import { Form, Button, Card, Container } from 'react-bootstrap'
import {auth} from '../../services/firebase-config'
export default function SignUp() {
const [registerEmail, setRegisterEmail] = useState("")
const [registerPassword, setRegisterPassword] = useState("")
const [registerConfirmedPassword, setRegisterConfirmedPassword] = useState("")
const [user, setUser] = useState({})
onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser)
})
const register = async (event) => {
event.preventDefault();
try {
const user = await createUserWithEmailAndPassword(auth, registerEmail, registerPassword)
console.log(user)
}
catch(error) {
//accounts that don't exist
console.log(error.message)
}
}
const login = async(event) => {
event.preventDefault();
try {
const user = await signInWithEmailAndPassword(auth, registerEmail, registerPassword)
console.log(user)
}
catch(error) {
//accounts that don't exist
console.log(error.message)
}
}
const logout = async(event) => {
try {
await signOut(auth)
}
catch(error) {
console.log(error.message)
}
}
return (
<Container className="d-flex align-items-center justify-content-center w-100">
<Card className='p-3' style={{maxWidth:"400px"}}>
<Card.Body>
<h2 className='text-center mb-4'>Sign Up</h2>
<Form>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control type="email" onChange={e => setRegisterEmail(e.target.value)} required />
</Form.Group>
<Form.Group id="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" onChange={e => setRegisterPassword(e.target.value)} required />
</Form.Group>
<Form.Group id="password-confirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control type="password" onChange={e => setRegisterConfirmedPassword(e.target.value)} required />
</Form.Group>
{ <Button className='w-100' onClick={(e) => register(e)}>Sign Up</Button>}
</Form>
</Card.Body>
<div className='w-100 text-center mt-2'>
Already have an account? <Button className='w-100' onClick={(e) => login(e)}>Login</Button>
</div>
<Button onClick={(e) => logout(e)}>Logout</Button>
</Card>
</Container>
)
}
Value attr and Initial state was missing in the form.control
<Form>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control value={registerEmail} type="email" onChange={e => setRegisterEmail(e.target.value)} required />
</Form.Group>
<Form.Group id="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" value={registerPassword} onChange={e => setRegisterPassword(e.target.value)} required />
</Form.Group>
<Form.Group id="password-confirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control type="password" onChange={e => setRegisterConfirmedPassword(e.target.value)} required />
</Form.Group>
{ <Button className='w-100' onClick={(e) => register(e)}>Sign Up</Button>}
</Form>
Example - codesandbox

React Multiselect package is sending [object, Object] instead of key and value

I am trying to pass data in my Django back end from react front end. I am able to pass the data using some Multi-select from react. But the problem is I am sending label and value but when I try to print my data in front end and check the data what it is passing I got the result like [object Object] instead of [mylabel MyValue]
and I just want to pass the option value. I am new to react so don't know much about setState things. Can someone help me to do this?
Or any other easy way to pass multiple data in my API it would be much appreciated like I select HD and SD then in my backend I should get both value.
#This is my react code check the deliveryOption, setDeliveryOption
import axios from "axios";
import React, { useState, useEffect } from 'react'
import { LinkContainer } from 'react-router-bootstrap'
import { Table, Button, Row, Col, Form } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import FormContainer from '../components/FormContainer'
import { MultiSelect } from "react-multi-select-component";
import Select from 'react-select';
const UPLOAD_ENDPOINT = "http://127.0.0.1:8000/api/orders/create/products/";
const options = [
{ label: "HD 🍇", value: "HD" },
{ label: "SD 🥭", value: "SD" },
{ label: "DP 🍓", value: "DP" },
];
function VendorProductCreate() {
const [file, setFile] = useState(null);
const [name, setName] = useState("");
const [price, setPrice] = useState();
const [discount, setDiscount] = useState();
const [description, setDescription] = useState("");
const [subcategory, setSubcategory] = useState("");
const [countInStock, setCountInStock] = useState();
const [deliveryOption, setDeliveryOption] = useState([]);
// const [selected, setSelected] = useState([]);
const { userInfo } = useSelector((state) => state.userLogin);
const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData();
formData.append("avatar", file);
formData.append("name", name);
formData.append("price", price);
formData.append("discount", discount);
formData.append("description", description);
formData.append("subcategory", subcategory);
formData.append("countInStock", countInStock);
formData.append("deliveryOption", deliveryOption);
// formData.append("selected", selected);
const resp = await axios.post(UPLOAD_ENDPOINT, formData, {
headers: {
"content-type": "multipart/form-data",
Authorization: `Bearer ${userInfo.token}`,
},
});
console.log(resp.status)
};
return (
<FormContainer>
<form onSubmit={handleSubmit}>
<br></br>
<Link to='/productlist/vendor'>
<Button
variant='outline-info'>Go Back</Button>
</Link>
<FormContainer>
<br></br>
<h1>Merchant Product Upload</h1></FormContainer>
<br></br>
<br></br>
<Form.Group controlId='name'>
<Form.Label><strong> Product Name </strong> </Form.Label>
<Form.Control
required
type='text'
onChange={(e) => setName(e.target.value)}
value={name}
// placeholder='Enter Name'
>
</Form.Control>
</Form.Group>
<br />
<Form.Group controlId='subcategory'>
<Form.Label>Choose a Category</Form.Label>
<Form.Select aria-label="Default select example"
required
type='text'
value={subcategory}
onChange={(e) => setSubcategory(e.target.value)}>
<option value="LPG Cylinder">LPG Cylinder</option>
<option value="LPG Gas">LPG Gas</option>
</Form.Select>
</Form.Group>
<br />
<Form.Group controlId='price'>
<Form.Label><strong>Price</strong> </Form.Label>
<Form.Control
required
type='number'
// placeholder='Enter Address'
onChange={(e) => setPrice(e.target.value)}
value={price}
>
</Form.Control>
</Form.Group>
<br />
<Form.Group controlId='discount'>
<Form.Label><strong> Set Discount (Optional) </strong> </Form.Label>
<Form.Control
type='number'
onChange={(e) => setDiscount(e.target.value)}
value={discount}
// placeholder='Enter Name'
>
</Form.Control>
</Form.Group>
<br />
<Form.Group controlId='countInStock'>
<Form.Label><strong> Stock </strong> </Form.Label>
<Form.Control
type='number'
onChange={(e) => setCountInStock(e.target.value)}
value={countInStock}
// placeholder='Enter Name'
>
</Form.Control>
</Form.Group>
<br />
<Form.Label><strong style={{marginLeft: 10}}>Product Picture</strong> </Form.Label>
<Form.Control
type='file'
id='image-file'
label='Choose File'
onChange={(e) => setFile(e.target.files[0])}
>
</Form.Control>
<br />
<Form.Group controlId='description'>
<Form.Label><strong> Description </strong> </Form.Label>
<Form.Control
required
type='text'
onChange={(e) => setDescription(e.target.value)}
value={description}
// placeholder='Enter Name'
>
</Form.Control>
</Form.Group>
<br />
{/* <Form.Group controlId='deliveryOption'>
<Form.Label>Choose a Delivery</Form.Label>
<Form.Select aria-label="Default select example"
required
multiple
type='checkbox'
onChange={(e) => setDeliveryOption(e.target.value)}>
<option value="HD">HD</option>
<option value="SD">SD</option>
</Form.Select>
</Form.Group>
<br /> */}
{/* <h1>Select Fruits</h1>
<pre>{JSON.stringify(selected)}</pre> */}
{/* <MultiSelect
options={options}
value={selected}
onChange={setSelected}
labelledBy="Select"
/> */}
<Select
isMulti
name="colors"
options={options}
className="basic-multi-select"
classNamePrefix="select"
value={deliveryOption}
onChange={setDeliveryOption}
/>
<br></br>
<FormContainer><FormContainer><FormContainer>
<Button variant='outline-primary' type="submit" className='btn-sm' >
Upload Product
</Button></FormContainer>
</FormContainer></FormContainer>
</form>
</FormContainer>
);
}
export default VendorProductCreate;
#this is backend code using Django rest
#api_view(['POST'])
#permission_classes([IsVendor])
def vendorCreateProduct(request):
data = request.data
user = request.user.vendor
print(data['deliveryOption'])
print(data['selected'])
product = Product.objects.create(
user=user,
name=data['name'],
old_price = data['price'],
discount = data['discount'],
image = data['avatar'],
countInStock = data['countInStock'],
subcategory = Subcategory.objects.get_or_create(name=data['subcategory'])[0],
description=data['description'],
delivery_option= DeliveryOption.objects.get_or_create(name=data['deliveryOption'])[0],
)
serializer = ProductSerializer(product, many=False)
return Response(serializer.data)
I just want to save some multiple choice field with multiple select option using Django and react if you know any other easy and good approach then share

Why getting too many re-renders?

I have a Calculator component for calculating the exponent of a given base.
My code is as follows:
//Exponential Calculator (Power/Squre-Root/Cube-Root)
const Exponents=()=>{
const [result,setResult]=useState(null);
const [choice,setChoice]=useState("Power");
const [x,setX]=useState(null);
const [n,setN]=useState(null);
useEffect(()=>{
},[choice,x,n,result]);
const calcResult=()=>{
let res=1;
if(choice=="Power")
{
for(var i=1;i<=n;i++)
res*=x;
}
else if(choice=="SquareRoot")
res=Math.sqrt(x);
else
res=Math.cbrt(x);
setResult(res);
}
const handleChange=(e)=>{
reset();
setChoice(e.target.value);
}
function reset(){
setResult(null);
setX(null);
setN(null);
}
const choiceData=()=>{
if(choice==="Power"){
return {
name:"Power",
quantities:["Base","Exponent"],
disabled:false
}
}
else if(choice==="SquareRoot")
{
setN(0.50);
return{
name:"Square-Root",
quantities:["Base","Exponent"],
disabled:true
}
}
else if(choice==="CubeRoot")
{
setN(0.34);
return{
name:"Cube-Root",
quantities:["Base","Exponent"],
disabled:true
}
}
}
return(
<>
<Form>
<Form.Group className="mb-4" controlId="choice">
<Form.Label>Select the type of calculation</Form.Label>
<Form.Control
as="select"
className="select-custom-res"
onChange={(e) => handleChange(e)}
>
<option value="Power">Power</option>
<option value="SquareRoot">Square Root</option>
<option value="CubeRoot">Cube Root</option>
</Form.Control>
</Form.Group>
<Form.Group className="mb-4" controlId="text">
<Form.Text className="text">
<strong>
To find the {choiceData().name}, Enter the following values
</strong>
<br />
</Form.Text>
</Form.Group>
<Form.Group className="mb-4">
<Form.Label>{choiceData().quantities[0]}</Form.Label>
<Form.Control
onChange={(e) => setX(e.target.value)}
type="number"
placeholder={"Enter the Base"}
value={x === null ? "" : x}
/>
</Form.Group>
<Form.Group className="mb-4">
<Form.Label>{choiceData().quantities[1]}</Form.Label>
<Form.Control
onChange={(e) => setN(e.target.value)}
type="number"
placeholder={"Enter the Exponent"}
value={n === null ? "" : n}
disabled={choiceData().disabled}
/>
</Form.Group>
<Form.Group className="mb-4">
<Form.Control
readOnly
type="number"
placeholder={result === null ? "Result" : result + " "}
/>
</Form.Group>
</Form>
<div className="button-custom-grp">
<Button variant="primary" onClick={calcResult}>
Calculate
</Button>
<Button variant="dark" onClick={() => reset()} type="reset">
Reset
</Button>
</div>
</>
)
}
On changing the choice from Power to SquareRoot I get an error:Too many rerenders. Interestingly, when I remove the setState line in the choiceData function,the error vanishes. Although I have used useEffect to prevent re-renders, it's not working.
Issue
You call choiceData in the render return, which should be free of side-effects, like enqueueing state updates. When you call setN in the choiceData function it triggers a rerender, which calls choiceData again, which triggers a rerender... repeat ad nauseam.
Solution
I suggest converting choiceData into a chunk of state, and use an useEffect hook to update it and the n state depending on the choice state. In the render return, instead of calling a function to get a value, i.e. choiceData().quantities[0], you instead just access a property of the new choiceData state, i.e. choiceData.quantities[0].
const Exponents = () => {
const [result, setResult] = useState(null);
const [choice, setChoice] = useState("Power");
const [choiceData, setChoiceData] = useState({});
const [x, setX] = useState(null);
const [n, setN] = useState(null);
useEffect(() => {
if (choice === "Power") {
setChoiceData({
name: "Power",
quantities: ["Base", "Exponent"],
disabled: false
});
} else if (choice === "SquareRoot") {
setN(0.5);
setChoiceData({
name: "Square-Root",
quantities: ["Base", "Exponent"],
disabled: true
});
} else if (choice === "CubeRoot") {
setN(0.34);
setChoiceData({
name: "Cube-Root",
quantities: ["Base", "Exponent"],
disabled: true
});
}
}, [choice]);
useEffect(() => {
// is this effect used for anything?
}, [choice, x, n, result]);
const calcResult = () => {
let res = 1;
if (choice == "Power") {
for (var i = 1; i <= n; i++) res *= x;
} else if (choice == "SquareRoot") res = Math.sqrt(x);
else res = Math.cbrt(x);
setResult(res);
};
const handleChange = (e) => {
reset();
setChoice(e.target.value);
};
function reset() {
setResult(null);
setX(null);
setN(null);
}
return (
<>
<Form>
<Form.Group className="mb-4" controlId="choice">
<Form.Label>Select the type of calculation</Form.Label>
<Form.Control
as="select"
className="select-custom-res"
onChange={(e) => handleChange(e)}
>
<option value="Power">Power</option>
<option value="SquareRoot">Square Root</option>
<option value="CubeRoot">Cube Root</option>
</Form.Control>
</Form.Group>
<Form.Group className="mb-4" controlId="text">
<Form.Text className="text">
<strong>
To find the {choiceData.name}, Enter the following values
</strong>
<br />
</Form.Text>
</Form.Group>
<Form.Group className="mb-4">
<Form.Label>{choiceData.quantities[0]}</Form.Label>
<Form.Control
onChange={(e) => setX(e.target.value)}
type="number"
placeholder={"Enter the Base"}
value={x === null ? "" : x}
/>
</Form.Group>
<Form.Group className="mb-4">
<Form.Label>{choiceData.quantities[1]}</Form.Label>
<Form.Control
onChange={(e) => setN(e.target.value)}
type="number"
placeholder={"Enter the Exponent"}
value={n === null ? "" : n}
disabled={choiceData.disabled}
/>
</Form.Group>
<Form.Group className="mb-4">
<Form.Control
readOnly
type="number"
placeholder={result === null ? "Result" : result + " "}
/>
</Form.Group>
</Form>
<div className="button-custom-grp">
<Button variant="primary" onClick={calcResult}>
Calculate
</Button>
<Button variant="dark" onClick={() => reset()} type="reset">
Reset
</Button>
</div>
</>
);
};

How to add edit button and function in react.js

i want to share this question. So, i have a form which is designed by react-bootstrap. And also use React Hooks and React Function Component. I have a form which is add new form and delete form but i don't do edit form.
This is a return statement
return(
<Container>
<Row>
<Col>
<Form onSubmit={handleSubmit}>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control ref = {firstname} type="text" placeholder="Name.." />
</Form.Group>
<Form.Group>
<Form.Label>Surname</Form.Label>
<Form.Control ref = {secondname} type="text" placeholder="Surname.." />
</Form.Group>
<Form.Group>
<Form.Label>Email address</Form.Label>
<Form.Control ref = {email} type="email" placeholder="E-Mail" />
<Form.Text> Please, Enter like "asd#asd.com"</Form.Text>
</Form.Group>
<Form.Group>
<Form.Label>Comment</Form.Label>
<Form.Control ref = {comment} as="textarea" rows={3} placeholder = "Notes :)"/>
</Form.Group>
<Button className = "btn-lg" onClick={handleSubmit} variant="success" type="submit">Submit</Button>
</Form>
</Col>
</Row>
{Formss}
</Container>
)
And then, These are the function of this return
const Formss = input.map((item , index) =>
{
return(
<Lists key = {index} item = {item} index = {index} deleteFunc={handleDelete}/>
)
}
)
const handleSubmit = (event) => {
event.preventDefault();
const name = firstname.current.value
const surname = secondname.current.value
const mail = email.current.value
const mycomment = comment.current.value
const data = {id:id(),
name : name,
surname : surname,
mail : mail,
mycomment : mycomment}
if(data.name && data.surname && data.mail && data.mycomment){
setInput([...input, data])
firstname.current.value = ""
secondname.current.value = ""
email.current.value = ""
comment.current.value =""
}else{
console.log("oopss")
}
}
I use ref hook for handleSubmit. So, How to add edit button and edit function?
To be able to edit data, and to save it in state you can do it as in provided example. Then in handleSubmit function you can process your data further:
import React from "react";
import { Container, Row, Col, Form, Button } from "react-bootstrap";
const App = () => {
const handleSubmit = (e) => {
e.preventDefault();
console.log(state);
};
const initialState = {
firstname: "",
secondname: "",
email: "",
comment: "",
};
const [state, setState] = React.useState(initialState);
const handleChange = ({ target: { value, name } }) => {
setState({ ...state, [name]: value });
};
return (
<Container>
<Row>
<Col>
<Form onSubmit={handleSubmit}>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control
name="firstname"
value={state.firstname}
type="text"
placeholder="Name.."
onChange={handleChange}
/>
</Form.Group>
<Form.Group>
<Form.Label>Surname</Form.Label>
<Form.Control
name="secondname"
value={state.secondname}
type="text"
placeholder="Surname.."
onChange={handleChange}
/>
</Form.Group>
<Form.Group>
<Form.Label>Email address</Form.Label>
<Form.Control
value={state.email}
name="email"
type="email"
placeholder="E-Mail"
onChange={handleChange}
/>
<Form.Text> Please, Enter like "asd#asd.com"</Form.Text>
</Form.Group>
<Form.Group>
<Form.Label>Comment</Form.Label>
<Form.Control
name="comment"
value={state.comment}
as="textarea"
rows={3}
placeholder="Notes :)"
onChange={handleChange}
/>
</Form.Group>
<Button className="btn-lg" variant="success" type="submit">
Submit
</Button>
</Form>
</Col>
</Row>
</Container>
);
};
export default App;

React useState hook does not shows correct state of array

I have two functional components and from parent component I am creating set of controls dynamically. Based on each item created I want to delete them but every time last one is getting deleted. For example three rows created when I delete second or first on,last one was getting deleted.
Education.jsx
function Education(props) {
const blankEdu = { id: 0, name: "", percentage: "", year: "" };
const [eduState, setEduState] = useState([{ ...blankEdu }]);
const addEducation = () => {
setEduState([...eduState, { ...blankEdu }]);
};
function handleRemove(index) {
console.log(index);
if (eduState.length != 1) {
const updatedEdu = [...eduState];
updatedEdu.splice(index, 1);
setEduState([...updatedEdu]);
}
}
const handleEducationChange = (index, e, c) => {
const updatedEdu = [...eduState];
updatedEdu[index][c] = e.target.value;
updatedEdu[index]["id"] = index;
setEduState(updatedEdu);
};
return (
<div>
<div className="shadow p-3 mb-5 bg-white rounded">
Final Step: Education
</div>
{eduState.map((val, idx) => (
<div
key={idx}
>
<EducationInput
key={`edu-${idx}`}
idx={idx}
handleEducationChange={handleEducationChange}
/>
{eduState.length > 1 ? (
<Button variant="danger" onClick={() => handleRemove(idx)}>
Remove Course
</Button>
) : null}
</div>
))}
<Button variant="outline-info" onClick={addEducation}>
Add New Degree
</Button>
</div>
);
}
export default Education;
EducationInput.jsx
const EducationInput = ({ idx, handleEducationChange }) => {
return (
<div key={`edu-${idx}`} id={`edu-${idx}`}>
<span className="border border-success">
<Form>
<Form.Group as={Row}>
<Form.Label column sm={3}>
{`Course #${idx + 1}`}:
</Form.Label>
<Col sm={5}>
<input
type="text"
onChange={e => handleEducationChange(idx, e, "name")}
/>
</Col>
</Form.Group>
<Form.Group as={Row}>
<Form.Label column sm={3}>
Passing Year:
</Form.Label>
<Col sm={5}>
<input
type="text"
onChange={e => handleEducationChange(idx, e, "year")}
/>
</Col>
</Form.Group>
</Form>
</span>
</div>
);
};
export default EducationInput;
I checked and verified value of updatedEdu by printing on console. It is giving correct output on console but setEduState function does not updating properly on UI, don't know why.
You are depending the index of the item, but indexes are changing when you add or remove elements, so they are not reliable.
You need to generate an automatic unique id when creating a new education.
For example uuid package is popular for this task.
I refactored your code a little bit to make it work:
Education:
import React, { useState } from "react";
import EducationInput from "./EducationInput";
import Button from "react-bootstrap/Button";
import uuidv4 from "uuid/v4";
function Education(props) {
const blankEdu = { id: "", name: "", percentage: "", year: "" };
const [eduState, setEduState] = useState([{ ...blankEdu }]);
const addEducation = () => {
setEduState([...eduState, { ...blankEdu, id: uuidv4() }]);
};
function handleRemove(id) {
console.log(id);
if (eduState.length > 1) {
const updatedEdus = eduState.filter(edu => edu.id !== id);
setEduState(updatedEdus);
}
}
const handleEducationChange = (id, field, value) => {
console.log(field, value);
let updatedEducations = eduState.map(edu => {
if (edu.id === id) return edu;
edu[field] = value;
return edu;
});
setEduState(updatedEducations);
};
return (
<div>
<div className="shadow p-3 mb-5 bg-white rounded">
Final Step: Education
</div>
{eduState.map(val => (
<div key={val.id}>
<EducationInput
key={`edu-${val.id}`}
idx={val.id}
handleEducationChange={handleEducationChange}
/>
{eduState.length > 1 ? (
<Button variant="danger" onClick={() => handleRemove(val.id)}>
Remove Course
</Button>
) : null}
</div>
))}
<Button variant="outline-info" onClick={addEducation}>
Add New Degree
</Button>
<br />
<br />
Educations in json:{JSON.stringify(eduState)}
</div>
);
}
export default Education;
EducationInput
import React from "react";
import Form from "react-bootstrap/Form";
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";
const EducationInput = ({ idx, handleEducationChange }) => {
return (
<div key={`edu-${idx}`} id={`edu-${idx}`}>
<span className="border border-success">
<Form>
<Form.Group as={Row}>
<Form.Label column sm={3}>
{`Course #${idx + 1}`}:
</Form.Label>
<Col sm={5}>
<input
type="text"
name="name"
onChange={e =>
handleEducationChange(idx, e.target.name, e.target.value)
}
/>
</Col>
</Form.Group>
<Form.Group as={Row}>
<Form.Label column sm={3}>
Passing Year:
</Form.Label>
<Col sm={5}>
<input
type="text"
name="year"
onChange={e =>
handleEducationChange(idx, e.target.name, e.target.value)
}
/>
</Col>
</Form.Group>
</Form>
</span>
</div>
);
};
export default EducationInput;
Codesandbox

Resources