I am trying to reverse a state to true or false when user is clicking the cancel button.
The full project is available on sandbox via the link below. I am new to react developing and struggling with state. Can someone help, please?
To see what i mean by the above, please enter some input and click add
https://codesandbox.io/s/eloquent-feather-gc88n?file=/src/header/header.js
Thank Leo
Update your handleCancelClick function to this:
handleCancelClick = (e) => {
setIsEditing((prevState) => !prevState)
console.log('Cancel edit', isEditing)
}
Few more amends that you might need:
In skillist.js:
<EditSkillsForm
inputs={inputs}
isEditing={isEditing}
setIsEditing={setISEditing}
onCancelClick={handleCancelClick}
onUpdateInput={handleUpdateInput}
onCurrentInput={currentInput}
/>
In editSkillsForm.js, we get isEditing and setIsEditing props also :
const EditSkillsForm = ({
handleUpdateInput,
inputs,
handleCancelClick,
setCurrentSkill,
isEditing,
setIsEditing
}) => {
Full file code (just in case):
editSkillsForm.js:
import React, { useState } from "react";
const EditSkillsForm = ({
handleUpdateInput,
inputs,
handleCancelClick,
setCurrentSkill,
isEditing,
setIsEditing
}) => {
//const [ currentSkill, setCurrentSkill ] = useState({});
// const [isEditing, setIsEditing] = useState(true);
// const [ onSubmitEditing, SetOnSubmitEditing ] = useState("")
function handleEditInputChange(e) {
setCurrentSkill(e.target.value);
}
handleCancelClick = (e) => {
setIsEditing(false);
console.log("Cancel edit", isEditing);
};
return (
<>
{isEditing ? (
<div>
<h4>Edit Skill</h4>
<input
className="mb-2"
size="lg"
onChange={handleEditInputChange}
type="text"
name="Update skill"
placeholder="Update skill"
value={inputs}
/>
<button className="btn btn-primary mx-2" onClick={handleUpdateInput}>
Update
</button>
<button onClick={() => handleCancelClick()}>Cancel</button>
</div>
) : null}
{/* <div>
<h4>Edit Skill</h4>
<input
className="mb-2"
size="lg"
onChange={handleEditInputChange}
type="text"
name="Update skill"
placeholder="Update skill"
value={inputs}
/>
</div> */}
{/* <input
className="mb-2"
size="lg"
onChange={handleEditInputChange}
type="text"
name="Update skill"
placeholder="Update skill"
value={inputs}
/> */}
{/* <button className="btn btn-primary mx-2">Update</button> */}
{/* <button onClick={() => handleCancelClick()}>Cancel</button> */}
</>
);
};
export default EditSkillsForm;
Related
I have a simple todo list that consists of multiple inputs.
I made the editing functionality and now everything works as it should, but only once. When I change the input data for the first time, it saves everything to an array with the correct data.
And when I want to do it a second time, then in order to save this data, three inputs must be changed.
I want that even when changing one input, the data is saved in an array (data that has not been changed is overwritten).
Stackblitz code
App.js
function App(props) {
const [tasks, setTasks] = useState(props.tasks);
function editTask(id, newName, newTranslate, newNote) {
const editedTaskList = tasks.map((task) => {
if (id === task.id) {
return { ...task, name: newName , translate: newTranslate , note: newNote };
}
return task;
});
setTasks(editedTaskList);
}
const taskList = tasks
.map((task) => (
<Todo
id={task.id}
name={task.name}
translate={task.translate}
note={task.note}
completed={task.completed}
key={task.id}
editTask={editTask}
tasks={tasks}
/>
));
return (
<div className="todoapp stack-large">
<ul
className="todo-list stack-large stack-exception"
aria-labelledby="list-heading">
{taskList}
</ul>
</div>
);
}
export default App;
I did a check and added the save button onClick which outputs the data to the console. It gives the data correctly the first time, and if the same item in the todo is changed the second time, it gives an empty space instead of the data that has not been changed.
Todo.js
export default function Todo({name, translate, note, editTask, id, tasks}) {
const [isEditing, setEditing] = useState(false);
const [newName, setNewName] = useState(name);
const [newTranslate, setNewTranslate] = useState(translate);
const [newNote, setNewNote] = useState(note);
function handleChange(e) {
setNewName(e.target.value)
}
function handleChangeTranslate(e) {
setNewTranslate(e.target.value);
}
function handleChangeNote(e) {
setNewNote(e.target.value)
}
function handleSubmit(e) {
e.preventDefault();
if (!newName.trim()|| !newTranslate.trim() || !newNote.trim()) {
return;
}
editTask(id, newName,newTranslate,newNote);
setNewName("");
setNewTranslate("");
setNewNote("");
setEditing(false);
}
const editingTemplate = (
<form className="stack-small" onSubmit={handleSubmit}>
<div className="form-group">
<input
id={id}
className="todo-text"
type="text"
autoComplete='off'
defaultValue={newName || name}
onChange={handleChange}
placeholder="write word"
/>
<input
id={id}
className="todo-text"
type="text"
autoComplete='off'
defaultValue={newTranslate || translate}
onChange={handleChangeTranslate}
placeholder="write translate"
/>
<input
id={id}
className="todo-text"
type="text"
autoComplete='off'
defaultValue={newNote || note}
onChange={handleChangeNote}
placeholder="write note"
/>
</div>
<div className="btn-group">
<button
type="button"
className="btn todo-cancel"
onClick={() => setEditing(false)}
>
Cancel
</button>
<button type="submit" className="btn btn__primary todo-edit" onClick={()=>console.log(newName, newTranslate, newNote)}>
Save
</button>
</div>
</form>
);
const viewTemplate = (
<div className="stack-small">
<div className="c-cb">
<label className="todo-label" htmlFor={id}>
{name}
</label>
<label className="todo-label" htmlFor={id}>
{translate}
</label>
<label className="todo-label" htmlFor={id}>
{note}
</label>
</div>
<div className="btn-group">
<button
type="button"
className="btn"
onClick={() => setEditing(true)}
>
Edit <span className="visually-hidden">{name}</span>
</button>
</div>
</div>
);
return <li className="todo">{isEditing ? editingTemplate : viewTemplate}</li>;
}
Since you want to keep those preview state which was not edit and still print out those state with the one you edit, you can just remove all the "reset state '' you put, since all your initial state from useState already had a value and is not an empty string "" like this
function handleSubmit(e) {
e.preventDefault();
if (!newName.trim()|| !newTranslate.trim() || !newNote.trim()) {
return;
}
editTask(id, newName,newTranslate,newNote);
setEditing(false);
}
I have state,
const [state, setState] = useState({
semester: '',
credit: '',
sgpa: ''
})
I have taken an input field where user can give his value.According to the user input dynamicallyinputfield will be created.So here my issue is 'I want different data from each dynamically created field'.How can I achive that?
Screenshot of form
my whole component is,
import React, { useState } from "react";
import cal from "./image/bgimg.jpg";
function Home() {
const [state, setState] = useState({
semester: "",
credit: "",
sgpa: "",
});
const [noOfSem, setNoOfSem] = useState([]);
const handleChange = (e) => {
setState({ ...state, [e.target.name]: e.target.value });
};
const handleClick = () => {
let sems = [];
for (let index = 0; index < state?.semester; index++) {
sems.push(index + 1);
}
console.log(sems);
setNoOfSem(sems);
};
const emptySemester = () => {
setState({ ...state, semester: "" });
setNoOfSem([]);
};
const handleCgpaSubmit = () => {
console.log(state.credit, state.sgpa);
};
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<img src={cal} alt="" className="imgcal img-fluid" />
</div>
<div className="col-md-6">
<div className="col-md">
<div className="form1">
<div className="col-md formmain">
<input
type="number"
value={state?.semester}
name="semester"
onChange={handleChange}
placeholder="Enter Total Semester"
/>
{noOfSem.length === 0 ? (
<button
type="button"
className="btn btn-success btn333"
onClick={handleClick}
>
Submit
</button>
) : (
<button
type="button"
className="btn btn-success btn333"
onClick={emptySemester}
>
Reset Semester
</button>
)}
</div>
<div className="col form2">
{noOfSem?.map((item, index) => (
<>
<div className="col-md seminpt">
<label htmlFor="">Semester {index + 1}</label>
<input
type="Number"
name="credit"
value={state.credit}
onChange={handleChange}
placeholder="Total Credit"
/>
<input
type="Number"
name="sgpa"
value={state.sgpa}
onChange={handleChange}
placeholder={`SGPA sem${index + 1}`}
/>
</div>
</>
))}
{noOfSem.length > 0 && (
<button
type="button"
className="btn btn-success btn3334"
onClick={handleCgpaSubmit}
>
Submit
</button>
)}
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default Home;
'I want different data from each dynamically created field'.How can I achive that?
Thanks in advance....
I changed the input for the number of semester to have a ref, since there is no need to update a state everytime you change the input. When calling handleClick it will take the value of the input and update noOfSem.
The handleChange takes in a semester index, this will help keep track of which data belongs to which semester. We return a new state with and override the prevState on the given semesterIndex.
When rendering the we just create a new array the size of noOfSem. Getting the values from the state we simply pass in the semesterIndex and pick the property we want.
function Home() {
const [state, setState] = useState({});
const [noOfSem, setNoOfSem] = useState(0);
const semesterNumberInputRef = useRef(null);
const handleChange = (e, semesterIndex) => {
setState((prevState) => {
return {
...prevState,
[semesterIndex]: {
...prevState[semesterIndex],
[e.target.name]: e.target.value,
},
};
});
};
const handleClick = () => {
if (!semesterNumberInputRef.current) return;
const newNumberOfSemesters = Number(semesterNumberInputRef.current.value);
setNoOfSem(newNumberOfSemesters);
};
const emptySemester = () => {
setState({});
setNoOfSem(0);
};
const handleCgpaSubmit = () => {
console.log(state);
};
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<div className="col-md">
<div className="form1">
<div className="col-md formmain">
<input
ref={semesterNumberInputRef}
type="number"
name="semester"
placeholder="Enter Total Semester"
/>
{noOfSem === 0 ? (
<button
type="button"
className="btn btn-success btn333"
onClick={handleClick}
>
Submit
</button>
) : (
<button
type="button"
className="btn btn-success btn333"
onClick={emptySemester}
>
Reset Semester
</button>
)}
</div>
<div className="col form2">
{new Array(noOfSem).fill(null).map((_, index) => {
const semesterIndex = index + 1; // add one since the index starts at 0
return (
<div
className="col-md seminpt"
key={`semester-input-${index}`}
>
<label>Semester {index + 1}</label>
<input
type="number"
name="credit"
value={state[semesterIndex]?.credit || ""}
onChange={(e) => handleChange(e, semesterIndex)}
placeholder="Total Credit"
/>
<input
type="number"
name="sgpa"
value={state[semesterIndex]?.sgpa || ""}
onChange={(e) => handleChange(e, semesterIndex)}
placeholder={`SGPA sem${semesterIndex}`}
/>
</div>
);
})}
{noOfSem > 0 && (
<button
type="button"
className="btn btn-success btn3334"
onClick={handleCgpaSubmit}
>
Submit
</button>
)}
</div>
</div>
</div>
</div>
</div>
</div>
);
}
handleCgpaSumbit will log this as result, an object with the number of the semester as index and the according data.
{
"1": {
"credit": "20",
"sgpa": "10"
},
"2": {
"credit": "21",
"sgpa": "9"
},
"3": {
"credit": "13",
"sgpa": "6"
},
"4": {
"credit": "8",
"sgpa": "6"
}
}
I hope this helps you with your project!
I can't add an image while executing. When adding an image, the image does not appear, I use a dynamic form with the same inputs when adding a form which will later be stored by looping, but I have problems when adding images. I use a plugin from ant design.
Here's the code.
import React,{useState} from 'react'
import { Button,Form,Input } from 'antd';
import {
PlusOutlined,
MinusCircleOutlined,
} from '#ant-design/icons';
function Ad_abhb() {
const [Img1,setImg] = useState([]);
const i = 0;
const AddImg4 = (i) =>{
const [imgPrev1,setImgPrev1] = useState('');
const ImgC1=(e)=>{
const reader = new FileReader();
const vImg = e.target.files[0];
setImgPrev1(URL.createObjectURL(vImg));
reader.readAsDataURL(vImg);
reader.onload = () => {[...setImg(reader.result)]}
}
return(<>
<Form.Item className='imgHobi'>
<Input id={'uImg4'+i.kd} type="file" accept=".jpg,.jpeg,.png" onChange={ImgC1} hidden />
<label htmlFor={'uImg4'+i.kd}>
{imgPrev1.length >= 1 ?<>
<div className='imgLoad'>
<img src={imgPrev1} className='getImg' alt=''/>
</div>
</>:<>
<div className='imgLoad'>
<div className='UpImg'>
<div className='UpCore'>
<PlusOutlined style={{fontSize:'20px'}}/>
</div>
</div>
</div>
</>}
</label>
</Form.Item>
</>)
}
const SimpanA4=()=>{
console.log(Img1)
}
return (<>
<h1>hobi</h1>
<div className='konten'>
<Form >
<Form.List name='Hobi'>
{(fields, { add, remove }) => (<>
{fields.map((field,i)=>{
return(
<div key={field.key} className='cardAdminMod hobiList' >
<AddImg4 kd={i}/>
<Form.Item className='inputHobi' name="Hobi" {...field}>
<Input />
</Form.Item>
{fields.length > 1 ? <div className='rmHobi'><MinusCircleOutlined onClick={() => remove(field.name)} /></div>: null}
</div>)
})}
<Form.Item className='addPlusHobi'>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add Data
</Button>
</Form.Item>
</>)}
</Form.List>
</Form>
</div>
<div className="titleAdmin butSide">
<Button onClick={SimpanA4}>Save</Button>
</div>
</>)
}
export default Ad_abhb;
So, can anyone help me?
I am trying to edit user input but the click event point to the input at array 0. i wanted to be able to render only the input to be edited.
The full project is available on sandbox via the link below. I am new to react developing and struggling with state. Can someone help, please?
To see what i mean by the above, please enter some input and click add 2 different input then edit the car. Only the inputs 0 is rendered.
https://codesandbox.io/s/eloquent-feather-gc88n?file=/src/header/header.js
Thank Leo
import React, { useState } from "react";
const EditSkillsForm = ({
handleUpdateInput,
inputs,
handleCancelClick,
//setCurrentSkill,
}) => {
const [currentSkill, setCurrentSkill] = useState({});
const [isEditing, setIsEditing] = useState(true);
// const [ onSubmitEditing, SetOnSubmitEditing ] = useState("")
function handleEditInputChange(e) {
setCurrentSkill(e.target.value);
}
handleCancelClick = (e) => {
setIsEditing(false);
console.log("Cancel edit");
//return {...prevState, isEditing : isEditing }
};
return (
<>
{isEditing ? (
<div>
<h4>Edit Skill</h4>
<input
className="mb-2"
size="lg"
onChange={handleEditInputChange}
type="text"
name="Update skill"
placeholder="Update skill"
value={[inputs[0]]}
/>
<button className="btn btn-primary mx-2" onClick={handleUpdateInput}>
Update
</button>
<button onClick={() => handleCancelClick()}>Cancel</button>
</div>
) : null}
</>
);
};
export default EditSkillsForm;
If you are editing 1 element at a time you shouldn't pass all the inputs.
Only pass the object you wanna show/permit to edit.
const EditSkillsForm = ({
handleUpdateInput,
input,
handleCancelClick,
//setCurrentSkill,
})
...
<input
className="mb-2"
size="lg"
onChange={handleEditInputChange}
type="text"
name="Update skill"
placeholder="Update skill"
value={input}
/>
{article.map((ArticleComp, i) =>
<div className="light-blue p-4 mb-3 mt-3" key={i}>
{ArticleComp}
<button type="button" className="red-button ml-2" onClick={() => removeArticle(i)}>Delete</button>
</div>
)}
<div className="mt-4">
<button type="button" className="blue-button" onClick={() => setAddArticle(true)}>Add Job</button>
<button type="button" className="blue-button ml-2" onClick={() => { setArticle([...article, <Article create="true" post={addArticle} updateError={props.updateError} updateArticle={UpdateArticleCallback}/>]) }}>Add Article</button>
<Link to="/job/all"><button type="button" className="red-button ml-2">Cancel</button></Link>
</div>
export default function Article(props) {
const [title, setTitle] = useState("")
const [content, setContent] = useState("")
if (props.edit === "true" || props.create === "true") {
if (props.post !== "no") {
axios.post('/api/v1/article/create.php', {
title: title,
content: content,
job_id: props.post
}).then((response) => {
if(response.data.Message === 'OK'){
props.updateError(null)
props.updateArticle()
} else {
props.updateError(response.data.Error)
}
})
}
return (
<Row>
{props.post}
<Col>
<Form.Group>
<Form.Label>Article Title</Form.Label>
<Form.Control key="articleTitle" type="text" placeholder="Title" value={title} onChange={e => setTitle(e.target.value)} maxLength="200" />
</Form.Group>
<Form.Group>
<Form.Label>Article Content</Form.Label>
<Form.Control as="textarea" rows={3} key="articleContent" placeholder="Alert Content" value={content} onChange={e => setContent(e.target.value)} maxLength="500" />
</Form.Group>
</Col>
</Row>
)
} else {
return (
<>
{props.article.map((article, i) =>
<div key={i} className="light-blue p-4">
<Row><Col><h3>{article.title}</h3><p>{article.content}</p></Col></Row>
</div>
)}
</>
)
}
}
I am passing the state variable addArticle as a prop however when I update the state, the component does not update. The component works as expected when not in the article state and then being mapped out. Shown is the Article component being added to the article state which is then being rendered. I rendered the props.post in the Article component so I could visually see the state updating. The props.post only takes the latest state when a new article is appended to the state
Thanks