how to pass input data to another component in react - reactjs

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

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

Set state false to checked radio buttons in map Component

function EachData({ data, index, open }) {
const [isOpen, setisOpen] = useState(open);
const toggle = (e) => {
setisOpen((prev) => !prev);
};
return (
<tr>
{/*<td></td>'s ... */}
<td>
<div className="functions">
{!isOpen ? (
<>
<label className="far fa-edit" htmlFor={`label-${index + 1}`}>
<input type="radio" name="edit" id={`label-${index + 1}`} onChange={toggle} />
</label>
<label className="far fa-trash"></label>
</>
) : (
<>
<label className="far fa-circle-check"></label>
<label className="far fa-times-circle" htmlFor={`label-${index + 1}`} >
<input type="radio" name="edit" id={`label-${index + 1}`} onChange={toggle} />
</label>
</>
)}
</div>
</td>
</tr>
);
}
export default EachData;
App.js
array.map((data, index)=>{
return(
<EachData data={data} index={index} isOpen={false}/>
)
})
When I check the radio buttons the jsx changes as expected, but after checking another radio button the previous one's state remains true. How do I set those elements state to false ?
You should store isOpen state in your array data, not in EachData component
const [array, setArray] = useState();
const toggle = idx => {
const newArray = array.map((item, index) => {
if (idx == index) return {
...item,
isOpen: !item.isOpen
}
return item
})
setArray(newArray);
}
array.map((data, index) => {
return <EachData data={data} index={index} isOpen={data.isOpen} toggle={toggle} />;
});
function EachData({ data, index, isOpen, toggle }) {
return (
<tr>
{/*<td></td>'s ... */}
<td>
<div className="functions">
{!isOpen ? (
<>
<label className="far fa-edit" htmlFor={`label-${index + 1}`}>
<input
type="radio"
name="edit"
id={`label-${index + 1}`}
onChange={() => toggle(index)}
/>
</label>
<label className="far fa-trash"></label>
</>
) : (
<>
<label className="far fa-circle-check"></label>
<label className="far fa-times-circle" htmlFor={`label-${index + 1}`}>
<input
type="radio"
name="edit"
id={`label-${index + 1}`}
onChange={() => toggle(index)}
/>
</label>
</>
)}
</div>
</td>
</tr>
);
}
You should use useRef instead of id, here I make some logic with useRef I hope this would be helpful.
As per my understand, when you click on radio button in map function this will be activated and when you click another radio button previous radio button is still showing active, In this code I create two useRef as you see the below code, one is taking for all indexes and second for removing previous radio button active. I hope you understand this code, if you know DOM.
function EachData({ data, index, open }) {
const [isOpen, setisOpen] = useState(open);
const radioRef = useRef([]);
const previousRadioRef = useRef([]);
const toggle = (i) => {
setisOpen((prev) => !prev);
if (previousRadioRef.current && previousRadioRef.current[0] !== radioRef.current[i]) {
if (radioRef.current[i]) {
if (previousRadioRef.current.length) {
previousRadioRef.current[0].checked = false;
previousRadioRef.current = [];
}
radioRef.current[i].checked = true;
previousRadioRef.current.push(radioRef.current[i]);
}
} else if(previousRadioRef.current && previousRadioRef.current[0]) {
previousRadioRef.current[0].checked = false;
previousRadioRef.current = [];
}
};
return (
<>
<tr>
<td>
<div className="functions">
{!isOpen ? (
<>
<label className="far fa-edit">
<input type="radio" name="edit" ref={ref => (radioRef.current[index] = ref)} onChange={() => toggle(index)} />
</label>
<label className="far fa-trash"></label>
</>
) : (
<>
<label className="far fa-circle-check"></label>
<label className="far fa-times-circle">
<input type="radio" name="edit" ref={ref => (radioRef.current[index] = ref)} onChange={() => toggle(index)} />
</label>
</>
)}
</div>
</td>
</tr>
</>
);
}
export default EachData;
array.map((data, index)=>{
return(
<EachData data={data} index={index} open={false}/>
)
})
You can set state with the name of radio input.
Reference: http://react.tips/radio-buttons-in-reactjs/

How can I display item in ToDo List

How to make todo list in react.I am following some tutorial how to work with react. This code is using input for adding item to list . How can I add item over h3 element instead input element?
This code is working perfect , I am looking for another way . Thank you
Here is full code .
import { useState } from 'react'
import { v4 as uuidV4 } from 'uuid'
const Header = () => {
const [input, setInput] = useState('')
const [todos, setTodos ] = useState([])
const onInput = (e) => {
setInput(e.target.value)
console.log(input)
}
const onFormSubmit = (e) => {
e.preventDefault()
setTodos([...todos, {id: uuidV4(), title:input, completed:false}])
setInput('')
}
return (
<section className='header'>
<h1>ToDo List</h1>
<form onSubmit={onFormSubmit}>
<input
type="text"
placeholder='Add Item'
className='input'
value={input}
required
onChange={onInput} />
<button
className='btn'
type='submit' > Add </button>
</form>
<br /><br />
<ul>
{todos.map((todo) => (
<li className='todo-list'> // here is output
// <h3> { ? } </h3> it should go todo.title
// can you show me how, pls ?
<input
type="text"
value={todo.title}
className='list'
onChange={(e)=>e.preventDefault()} />
</li>
))}
</ul>
</section>
)
};
export default Header;
Get the title of the todo from the todo object passed to .map() function.
<h3>{todo.title}</h3>
// Get a hook function
const {useState} = React;
const Header = () => {
const [input, setInput] = useState("");
const [todos, setTodos] = useState([]);
const onInput = (e) => {
setInput(e.target.value);
//console.log(input);
};
const onFormSubmit = (e) => {
e.preventDefault();
setTodos([...todos, { id: Math.random(), title: input, completed: false }]);
setInput("");
};
return (
<section className="header">
<h1>ToDo List</h1>
<form onSubmit={onFormSubmit}>
<input
type="text"
placeholder="Add Item"
className="input"
value={input}
required
onChange={onInput}
/>
<button className="btn" type="submit">
{" "}
Add{" "}
</button>
</form>
<br />
<br />
<ul>
{todos.map((todo) => (
<li className="todo-list">
<h3> {todo.title} </h3>
<input
type="text"
value={todo.title}
className="list"
onChange={(e) => e.preventDefault()}
/>
</li>
))}
</ul>
</section>
);
};
// Render it
ReactDOM.render(
<Header />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Can we use a button to update value of state?

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

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