Can we use a button to update value of state? - reactjs

My apologies for such a menial question but I've been stuck on this for some time.
I'm looking to create a few buttons that contain a value. Once clicked, that value is then passed to state.
Here's what I'm working with:
export function ExperienceLevel() {
const [experience, setExperience] = useState(["test"])
const toggleExperience = (event) => {
setExperience(event.target.value)
}
return(
<div className="experienceContainer">
<p>{experience}</p>
<button onClick={toggleExperience} value="one"/>
<button onClick={toggleExperience} value="two"/>
<button onClick={toggleExperience} value="three"/>
<input onClick={toggleExperience} type="radio" value="oneB" />
</div>
)
}
I've included a radio input containing the same parameters and it works to update state, but not with the buttons. Is this even possible or am I doing something wrong?

export function ExperienceLevel() {
const [experience, setExperience] = useState(['test'])
const toggleExperience = (value) => {
setExperience(value)
}
return (
<div className="experienceContainer">
<p>{experience}</p>
<button onClick={()=>toggleExperience('one')} />
<button onClick={()=>toggleExperience('two')} />
<button onClick={()=>toggleExperience('three')} />
<input onClick={()=>toggleExperience('oneB')} type="radio" />
</div>
)
}

Certainly you can use button value as well as radio.
export function ExperienceLevel() {
const [experience, setExperience] = useState("test")
const handleExperience = (event) => {
setExperience(event.target.value)
}
return(
<div className="experienceContainer">
<p>{experience}</p>
<button onClick={handleExperience} value="one">Change to One</button>
<button onClick={handleExperience} value="two">Change to Two</button>
<button onClick={handleExperience} value="three">Change to Three</button>
<input onClick={handleExperience} type="radio" value="oneB">Change to One B
</div>
)
}

export function ExperienceLevel() {
const [experience, setExperience] = useState('test')
const toggleExperience = (value) => {
setExperience(value)
}
return (
<div className="experienceContainer">
<p>Current Experience: {experience}</p>
<input type="button" onClick={()=>toggleExperience('one')} value="one" />
<input type="button" onClick={()=>toggleExperience('two')} value="two" />
<input type="button" onClick={()=>toggleExperience('three')} value="three" />
<input type="radio" onClick={()=>toggleExperience('oneB')} value="oneB" />
</div>
)
}

Related

updating data in edit-form react

I have a kind of todo, but there are several lines in one object.
I need that when editing one of the fields and pressing the save button, the save will work.
Now, in order to save the changes, need to change all three inputs.
Here is my code in stakeblitz
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}
/>
));
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 thought that the problem was with the handlers in the todo file, most likely there need to get the previous data from the state, and if the field has not been changed, then use this data as changed in the new state. I tried to do something like this but I couldn't find anything.
Todo.js
export default function Todo(props) {
const [isEditing, setEditing] = useState(false);
const [newName, setNewName] = useState('');
const [newTranslate, setNewTranslate] = useState('');
const [newNote, setNewNote] = useState('');
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;
}
props.editTask(props.id, newName, newTranslate, newNote);
setNewName("");
setNewTranslate("");
setNewNote("");
setEditing(false);
}
const editingTemplate = (
<form className="stack-small" onSubmit={handleSubmit}>
<div className="form-group">
<label className="todo-label" htmlFor={props.id}>
New name for {props.name}
</label>
<input
id={props.id}
className="todo-text"
type="text"
value={newName || props.name}
onChange={handleChange}
/>
<input
id={props.id}
className="todo-text"
type="text"
value={newTranslate || props.translate}
onChange={handleChangeTranslate}
/>
<input
id={props.id}
className="todo-text"
type="text"
value={newNote || props.note}
onChange={handleChangeNote}
/>
</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">
Save
</button>
</div>
</form>
);
const viewTemplate = (
<div className="stack-small">
<div className="c-cb">
<label className="todo-label" htmlFor={props.id}>
{props.name}
</label>
<label className="todo-label" htmlFor={props.id}>
{props.translate}
</label>
<label className="todo-label" htmlFor={props.id}>
{props.note}
</label>
</div>
<div className="btn-group">
<button
type="button"
className="btn"
onClick={() => setEditing(true)}
>
Edit <span className="visually-hidden">{props.name}</span>
</button>
</div>
</div>
);
return <li className="todo">{isEditing ? editingTemplate : viewTemplate}</li>;
}
By trial and error, I found how to solve my problem.
It is necessary to transfer data from the array to the new state, which will be the initial data for it
Todo.js file
export default function Todo({name, translate, note, editTask, id}) {
const [isEditing, setEditing] = useState(false);
const [newName, setNewName] = useState(name);
const [newTranslate, setNewTranslate] = useState(translate);
const [newNote, setNewNote] = useState(note);

empty data instead of data from the array when changing the data in the input react js

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);
}

html2canvas does not show form's input properly

I'm trying to use html2canvas in order to take a screenshot of a form. The code is working but the input does not display properly for me. It wouldn't be a problem but I need to read the text from the image itself.
const TransactionForm = () => {
const [canvasArray, setCanvasArray] = useState([])
const captureRef = useRef();
const getScreenshotHandler = () => {
html2canvas(captureRef.current).then(canvas =>
setCanvasArray(
[canvas.toDataURL()]
),
);
};
const renderCanvas = () => {
return canvasArray.map((canvas, i) => {
return <img key={i} src={canvas} alt="screenshot" />;
});
};
return(
<>
<form>
<div ref = {captureRef} className="to-capture">
<label>sender bank number</label>
<input type="text" />
<br />
<label>address bank number</label>
<input type="text"/>
<br />
<label>how much money to send</label>
<input type="number"/>
<br />
</div>
<Button fluid onClick={getScreenshotHandler}>
send
</Button>
</form>
{renderCanvas()}
);
}
[![the form looks like this](https://i.stack.imgur.com/zvCxU.png)](https://i.stack.imgur.com/zvCxU.png)
[![and the image looks like this](https://i.stack.imgur.com/P8anY.png)](https://i.stack.imgur.com/P8anY.png)

how to pass input data to another component in react

this is what i want to make
If I press the edit button on App.tsx,
I want to display the data in input text in AddToto.tsx.
I would appreciate it if you could tell me what method to use.
this is App.tsx
const App: React.FC = (props) => {
const [todos, setTodos] = useState<Todo[]>([]);
const [users, setUsers] = React.useState([] as Voca[]);
const editData = (id: number) => {
const item = users.find((user) => user.id === id);
console.log(item.word);
};
return (
<div className="App">
<h1>add word</h1>
<NewTodo onAddTodo={todoAddHandler} />
<hr />
<h1>edit word</h1>
<AddTodo />
<hr />
<table>
{users.map((user, index) => (
<tr key={index}>
<td>{user.word}</td>
<td>{user.meaning}</td>
<td>{user.enrollmentDate}</td>
<td>{user.editDate}</td>
<td className="opration">
<button onClick={() => removeData(user.id)}>delete</button>
</td>
<td>
<button
className="btn btn-warning"
onClick={() => {
editData(user.id);
}}
>
edit
</button>{" "}
</td>
</tr>
))}
</table>
</div>
);
};
export default App;
this is AddTodo.tsx
class AddTodo extends Component {
render() {
return (
<form>
<div className="form-control">
<label htmlFor="word">word:</label>
<input type="text" id="word" />
<br />
<label htmlFor="todo-meaning">meaning:</label>
<input type="text" id="todo-meaning" />
</div>
<button type="submit">save</button>
</form>
);
}
}
export default AddTodo;
In this case,
I don't know how to pass data to another component...
In editData, once you've found the item, set state which is passed as a prop to AddTodo:
const [userBeingEdited, setUserBeingEdited] = useState<Voca | null>(null);
const editData = (id: number) => {
setUserBeingEdited(users.find((user) => user.id === id));
};
return (
// ...
<AddTodo userBeingEdited={userBeingEdited} />
// AddTodo.tsx
export default ({
userBeingEdited,
}: {
userBeingEdited: Voca | null,
}) => (
<form>
<div className='form-control'>
<label htmlFor='word'>word:</label>
<input type='text' id='word' defaultValue={userBeingEdited?.word} />
<br />
<label htmlFor='todo-meaning'>meaning:</label>
<input type='text' id='todo-meaning' defaultValue={userBeingEdited?.meaning} />
</div>
<button type='submit'>save</button>
</form>
);

React component is not re-rendered after the state is changed with a dropdown list [react hooks]

I have the following React component (using hooks), which lists a number of Tasks as a dropdown list. When an item is selected from the list, I want to display an Update form. This works only when an item is selected for the first time. When I select a new item, nothing happens (although console.log(e.target.value); prints the correct value). I store the selected task's id in st_taskId.
I wonder if you see any issues in the code below:
const ManageReviewTasks = props => {
const reviewRoundId = props.match.params.reviewRoundId;
const [st_taskId, set_taskId] = useState();
useEffect(() => {
if (props.loading == false && st_taskId == null)
props.fetchReviewTasksByReviewRound(reviewRoundId);
}, [reviewRoundId, st_taskId]);
if (props.loading == true) {
return <div>Loading...</div>;
}
return (
<>
{props.reviewTasks && (
<div>
<h3>Configure the Review Tasks</h3>
<br />
{
<div>
<div>
<h4>
Tasks for <span className="font-italic">students receiving</span> feedback:
</h4>
<select
className="form-control"
onChange={e => {
e.preventDefault();
console.log(e.target.value);
set_taskId(e.target.value);
}}>
<option>--SELECT--</option>
{Object.keys(props.reviewTasks).map(id => {
const task = props.reviewTasks[id];
{
if (task.isForStudent) {
return (
<option key={id} id={id} value={id}>
{task.title}
</option>
);
}
}
})}
</select>
</div>
{props.reviewTasks[st_taskId] && (
<UpdateReviewTaskForm task={props.reviewTasks[st_taskId]} />
)}
</div>
}
</div>
)}
</>
);
};
Below is the code for the UpdateReviewTaskForm component:
const UpdateReviewTaskForm = (props) => {
const [st_Title, set_Title] = useState(props.task.title);
const [st_Description, set_Description] = useState(RichTextEditor.createValueFromString(props.task.description, 'html'));
const [st_startDate, set_startDate] = useState(new Date(props.task.startDate.replace('-', '/')));
const [st_DueDate, set_DueDate] = useState(new Date(props.task.dueDate.replace('-', '/')));
const handleCancelClick = (event) => {
event.preventDefault();
history.goBack();
}
const onSubmit_saveTask = (e) => {
e.preventDefault();
props.updateReviewTask({
Id: props.task.id,
Title: st_Title,
Description: st_Description.toString('html'),
StartDate: format(st_startDate, 'DD/MM/YYYY'),
DueDate: format(st_DueDate, 'DD/MM/YYYY'),
})
}
if (props.loading)
return <div>Updating...</div>
return (
<div>
<br/>
<br/>
<div className="p-3 bg-light">
<h3 className="text-info">Update the Task:</h3>
{
props.task &&
<form onSubmit={onSubmit_saveTask}>
<div className="form-group">
<label>Enter the title</label>
<input
//placeholder="Enter a title..."
value={st_Title}
onChange={(event) => { set_Title(event.target.value) }}
className="form-control" />
</div>
<div className="form-group">
<label>Enter a description for the assessment</label>
<RichTextEditor
value={st_Description}
onChange={set_Description}
/>
</div>
<div className="form-group">
<label>Start date to start: </label>
<DatePicker
className="form-control"
selected={st_startDate}
onChange={(date) => set_startDate(date)}
/>
</div>
<div className="form-group">
<label>Due date to complete: </label>
<DatePicker
className="form-control"
selected={st_DueDate}
onChange={(date) => set_DueDate(date)}
/>
</div>
<br />
<button type="submit" className="btn btn-primary">Submit</button>
<button type="reset" className="btn btn-light" onClick={handleCancelClick}>Cancel</button>
</form>
}
</div>
</div>
)
}
Because you are using internal state in UpdateReviewTaskForm, even if this component re-render for the second time, its state will not be reset (to the default value props.task.title for example).
One way to force the state to reset is to use a key prop in UpdateReviewTaskForm like this :
{props.reviewTasks[st_taskId] && (
<UpdateReviewTaskForm key={st_taskId} task={props.reviewTasks[st_taskId]} />
)}
Another way is to use useEffect inside UpdateReviewTaskForm to run when props.task change
useEffect(() => {
// reset the state here
}, [props.task])

Resources