Set state false to checked radio buttons in map Component - reactjs

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/

Related

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

stacked dynamic inputs - reactjs

I have an input and a select starting when placing any value in the case of the input, it begins to generate dynamic statements and in the case of the input, dynamic inputs are generated but in decrement, everything works fine, I only need that the dynamic inputs that begin to appear are show one by one and not all piled up as I show in the picture.
problem image
how do i want it to be
import React, { useState } from "react";
//input dynamic
import Row from "./Row2";
let initialState = {
first: null,
arraySelect: []
};
function Pruebas(props) {
/*input dynamic */
const [rows, setRows] = useState([]);
const [initialeRow, setInitialRow] = useState({ nombre: "" });
const handleOnChange = (index, value) => {
const copy = rows.map((e, i) => {
if (i === index) {
e.nombre = value;
}
return e;
});
setRows([...copy]);
};
const handleOnAdd = () => {
if (initialeRow.nombre >= 1) {
setInitialRow({ nombre: initialeRow.nombre - 1 });
setRows([...rows, initialeRow]);
}
};
/////////////////////////////////////////////////////
const [input_multi, setInput_multi] = useState();
const [arraySelect, setarraySelect] = useState(initialState.arraySelect);
const [numberIni, setnumberIni] = useState(initialState.first);
const getArray = (value) => {
let arr = [];
{
let reco = Math.round(numberIni - parseInt(value));
console.log(reco);
if (parseInt(value) == numberIni) {
return false;
}
Array(reco)
.fill(1)
.map((value2, key) => {
arr.push(parseInt(value) + parseInt(key + 1));
});
}
return arr;
};
const setSelect = (value) => {
let isArray = getArray(value);
if (isArray) {
setarraySelect([...arraySelect, isArray]);
}
//input dynamyc
if (initialeRow.nombre >= 1) {
setInitialRow({ nombre: initialeRow.nombre - 1 });
setRows([...rows, initialeRow]);
}
};
//input dynamic
const handleInput_division = (event) => {
setInitialRow({ nombre: event.target.value });
};
const handleSubmit = (event) => {
event.preventDefault();
setnumberIni(event.target.numberIni.value);
};
const resetForm = () => {
setnumberIni(null);
setarraySelect([]);
};
return (
<div>
<form onSubmit={handleSubmit}>
<div class="row">
<label>PHASES</label>
<div class="col-sm-6">
<h6>enter a number</h6>
<div class="input-group ">
<input
type="number"
name="numberIni"
placeholder="0"
class="form-control"
value={input_multi}
onChange={(event) => setInput_multi(event.target.value)}
/>
<br />
<button type="submit" className="btn btn-success"> <i class="far fa-save"></i></button>
<br />
<div class="col-sm-6">
<h6>2 - # input dynamic</h6>
<div class="input-group ">
<select name='numberIni2' class='form-control' onChange={handleInput_division}>
<option value='no' selected>
Seleccione </option>
<option value='2'> 2</option>
<option value='3'>3
</option>
</select>
<br/>
</div>
</div>
</div>
</div>
</div>
</form>
<br />
<div class="col-sm-12 btn btn-primary">
<b>PHASES</b>
</div>
<br /> <br />
<div class="row">
<div class="col-sm-12">
{numberIni && (
<div class="col-sm-12">
<label>
<font size="2">
1° PHASES <br />
select a number : {" "}
</font>
</label>
<select onChange={(e) => setSelect(e.target.value)} name="" id="">
<option value="seleccione">Seleccione</option>
{Array(parseInt(numberIni))
.fill(1)
.map((value, key) => {
return <option value={key + 1}>{key + 1} equipment</option>;
})}
</select>
<label>
<font size="2"> equipment </font>{" "}
</label>
{Array(parseInt(numberIni))
.fill(1)
.map((value, key2) => {
return (
<div>
{arraySelect[key2] && (
<>
<label>
<font size="2">
2° select another number <br />
classify: {" "}
</font>{" "}
</label>
<select
onChange={(e) => setSelect(e.target.value)}
name=""
id=""
>
<option value="seleccione">Seleccione</option>
{arraySelect[key2].map((value, key3) => {
return (
<option value={arraySelect[key2][key3]}>
{arraySelect[key2][key3]} equipment
</option>
);
})}
</select>
{rows.map((e, index) => (
<Row
nombre={e.nombre}
index={index}
onChange={(index, value) => handleOnChange(index, value)}
key={index}
/>
))}
</>
)}
</div>
);
})}
</div>
)}
</div>
</div>
{numberIni && (
<input
onClick={() => resetForm()}
type="button"
className="btn btn-danger"
value="restart phase"
/>
)}
</div>
);
}
export default Pruebas;
//Row2
const Row = (props) => {
const { onChange, onRemove, nombre, index } = props;
console.log(props);
return (
<div>
<input
disabled
value={nombre}
onChange={(e) => onChange(index, e.target.value)}
placeholder="Decrementar"
/>
</div>
);
};
export default Row;

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

Why don´´t work onClick on option element

I'm developing a plugin to search streets with elastic search, ok?
I have a datalist to show my options to select.
When I receive info from database I create all html option elemtns and add click event to capture and handle.
But I dont know why not works onClick event that I've added to each option element.
Here is my code EDITED:
render() {
const { value, streets, error, labelError } = this.state;
return (
<div className="w-100 d-flex flex-column">
<div className="plug-search plug-search__content">
<div className="plug-inner-addon">
<input onKeyDown={this.handlePressEnter.bind(this)} onChange={this.handleSearch.bind(this)} type="text" placeholder={PLACEHOLDER} value={value} list="streets" autoComplete="on" />
<datalist id="streets">
{ streets && streets.length > 0 && streets.map((street, index) => {
return (
<Item street={street} position={index} key={index} />
);
})}
</datalist>
</div>
<div className="plug-btn-search plug-btn-search__content">
<i onClick={this.handleGetGeometry} className={`icon-search plug-search-icon`}></i>
</div>
</div>
{error &&
<div className={`plug-error plug-error__content ${(error) ? 'slideDown' : 'slideUp'}`}>
<label className="plug-label">{labelError}</label>
</div>
}
</div>
);
}
Now I've created a Item component, but still doesnt work:
class Item extends Component {
clickedOption = (event, index) => {
console.log('clicked');
console.log('value: ', event.target.value);
console.log('index: ', index);
};
render() {
return (
<div className="option" onClick={(event) => this.clickedOption(event, this.props.position)}>
<option value={this.props.street.nombre} />
</div>
)
}
}
export default Item;
Currently, <datalist /> don't support onClick events in his <options />, you could see this question, in order to apply an option in this kind of cases. Hope this help.
Thanks to Alberto Perez
Solved:
clickedOption = (event) => {
console.log('clicked');
console.log('value: ', event.target.value);
};
render() {
const { value, streets, error, labelError } = this.state;
return (
<div className="w-100 d-flex flex-column">
<div className="plug-search plug-search__content">
<div className="plug-inner-addon">
<input onInput={this.clickedOption} onChange={this.handleSearch.bind(this)} type="text" placeholder={PLACEHOLDER} value={value} list="streets" autoComplete="on" />
<datalist id="streets">
{ streets && streets.length > 0 && streets.map((street, index) => {
return (
<option value={street.nombre} key={index} />
);
})}
</datalist>
</div>
<div className="plug-btn-search plug-btn-search__content">
<i onClick={this.handleGetGeometry} className={`icon-search plug-search-icon`}></i>
</div>
</div>
{error &&
<div className={`plug-error plug-error__content ${(error) ? 'slideDown' : 'slideUp'}`}>
<label className="plug-label">{labelError}</label>
</div>
}
</div>
);
}
handleSearch = (e) => {
this.setState({
value: e.target.value
})
}
render() {
const { value, streets, error, labelError } = this.state;
return (
<div className="w-100 d-flex flex-column">
<div className="plug-search plug-search__content">
<div className="plug-inner-addon">
<input onKeyDown={this.handlePressEnter.bind(this)} onChange={this.handleSearch.bind(this)} type="text" placeholder={"placeholder"} value={value} list="streets" autoComplete="on" />
<datalist id="streets">
{ streets && streets.length > 0 && streets.map((street, index) => {
return (
<div key={index} className="option">
<option value={street.nombre} />
</div>
);
})}
</datalist>
</div>
<div className="plug-btn-search plug-btn-search__content">
<i onClick={this.handleGetGeometry} className={`icon-search plug-search-icon`}></i>
</div>
</div>
{error &&
<div className={`plug-error plug-error__content ${(error) ? 'slideDown' : 'slideUp' }`}>
<label className="plug-label">{labelError}</label>
</div>
}
</div>
);
}

Resources