React checkbox: Select one and disable others - reactjs

Initially every items should active but When someone click on any items the others will disable/opacity(0.5). How to do that, I have tried a lot but can't find any solution.
const handleCheckbox = (e) => {
setActive(!active)
let selected = [...selectedHobby]
if (e.target.checked) {
selected = [...selectedHobby, e.target.value]
} else {
selected.splice(selectedHobby.indexOf(e.target.value), 1)
}
setSelectedHobby(selected)
router.push({
pathname: '',
query: { ...router.query, 'search': selected }
})
}
return (<>
<div className={`${active ? 'deactive' : 'active'}`}>
<input
type="checkbox"
name={props.name}
value={props.value}
onChange={(e) => handleCheckbox(e)}
/> {props.label}
</div>
</>
)
}
.deactive {
opacity: 0.50;
}
Sandbox:
https://codesandbox.io/s/aged-butterfly-ch7u13?file=/src/App.js

Here is the solution.
import "./styles.css";
import React, { useState } from "react";
let fruits = ["orange", "lemon", "apple", "watermelon"];
const Check = (props) => {
const handleCheckbox = (name) => {
const findIndex = props.selectedHobby.findIndex((v) => v === name);
if (findIndex === -1) {
return props.setSelectedHobby((array) => [...array, name]);
}
return props.setSelectedHobby((array) => array.filter((v) => v !== name));
};
return (
<>
<div
className={`${
props.selectedHobby.length === 0
? "active"
: props.selectedHobby.includes(props.name)
? "active"
: "deactive"
}`}
>
<input
type="checkbox"
name={props.name}
value={props.value}
onChange={(e) => handleCheckbox(props.name)}
/>{" "}
{props.label}
</div>
</>
);
};
const HobbyCheck = () => {
const [selectedHobby, setSelectedHobby] = useState([]);
return (
<>
{fruits.map((fruit, key) => (
<Check
selectedHobby={selectedHobby}
setSelectedHobby={setSelectedHobby}
key={key}
label={fruit}
name={fruit}
/>
))}
</>
);
};
export default HobbyCheck;

Related

React - reduce strange interaction on input setState

I have a quiz and it has an arr of questions, a question has value.
I want to assign the total of questions question.value to the quiz.value as the value updates.
Here is how i am currently handling the updating (obviously i havent dealt with if a question is removed yet)
const handleUpdateQuestionScore = (e) => {
setQuiz({...quiz, questions: quiz.questions.map((child, index) => index === selectedQuestion ? { ...child, value: parseInt(e.target.value) } : child)})
setReUpdateTotal(true);
}
useEffect(() => { // this has weird behaviour
console.log("my total", quiz.questions.reduce((total, question) => total = total + question.value, 0))
if(quiz.questions.length < 1) {
setQuiz({ ...quiz, value: 0 } )
}
else{
setQuiz({...quiz, value: quiz.questions.reduce((total, question) => total = total + question.value, 0) } )
}
},[reUpdateTotal])
The issue with this is, when i set question 1, its correct.
When i add question 2 and set its value, sometimes it only interpolates the first number.
going back off the form and loading it up will update.
Other times it will not update the value at all.
For a much more indept demonstration, this codeSandbox demonstrates functionality:
https://codesandbox.io/s/stupefied-shadow-i9z2wx?file=/src/App.js
Here is the default code in the codeSandbox
import { useEffect, useState } from "react";
export default function App() {
const [quiz, setQuiz] = useState({
title: "",
number: 6,
questions: [
{
question: "q1",
value: 0
},
{
question: "q2",
value: 0
}
]
});
const [selectedQuestion, setSelectedQuestion] = useState(undefined);
useEffect(() => {
console.log("quiz", quiz);
}, [quiz]);
const [reUpdateTotal, setReUpdateTotal] = useState();
const handleUpdateQuestionScore = (e) => {
setQuiz({
...quiz,
questions: quiz.questions.map((child, index) =>
index === selectedQuestion
? { ...child, value: parseInt(e.target.value) }
: child
)
});
setReUpdateTotal(true);
};
useEffect(() => {
// this has weird behaviour
console.log(
"my total",
quiz.questions.reduce(
(total, question) => (total = total + question.value),
0
)
);
if (quiz.questions.length < 1) {
setQuiz({ ...quiz, value: 0 });
} else {
setQuiz({
...quiz,
value: quiz.questions.reduce(
(total, question) => (total = total + question.value),
0
)
});
}
}, [reUpdateTotal]);
return (
<div className="App">
<div>
{quiz.questions.map((question, index) => (
<div key={index}>
<p>
{question.question} - {question.value}
<button onClick={() => setSelectedQuestion(index)}>select</button>
</p>
<br />
</div>
))}
</div>
<br />
<small>change value for question #{selectedQuestion}</small>
{selectedQuestion !== undefined ? (
<div>
<input
type="number"
id="Value"
name="Value"
required="required"
className="edit-input shadow"
min="0"
value={quiz.questions[selectedQuestion].value}
onChange={(e) => {
handleUpdateQuestionScore(e);
}}
/>
<button onClick={() => setSelectedQuestion(undefined)}>remove</button>
</div>
) : (
<div>nothing selected</div>
)}
<h1>current total score = {quiz.value}</h1>
</div>
);
}
Technically - your useEffect that has a [reUpdateTotal] as a dependency array executes only once. When switching value from false to true. Im not seeing where you are switching it back. But still, you dont even need this variable and rely on the useEffect with [quiz] which will execute only 1 function - setQuiz. And no, it will not cause infinite rerenderings, if used correctly. It can accept a function with 1 parameter, which will be your "current" value of a quiz. I used prev but actually it is curr. And this function can return either a new value and fire a rerender, either the current value and do nothing in this case.
import "./styles.css";
import { useEffect, useState } from "react";
export default function App() {
const [quiz, setQuiz] = useState({
title: "",
number: 6,
questions: [
{ question: "q1", value: 0 },
{ question: "q2", value: 0 }
]
});
const [selectedQuestionIndex, setSelectedQuestionIndex] = useState(undefined);
const handleUpdateQuestionScore = (e) => {
setQuiz((prev) => {
prev.questions[selectedQuestionIndex].value = parseInt(
e.target.value,
10
);
return { ...prev };
});
};
useEffect(() => {
setQuiz((prev) => {
const currValue = prev.value;
const newValue =
prev.questions?.reduce(
(total, question) => (total = total + question.value),
0
) || 0;
if (newValue === currValue) return prev;
return {
...prev,
value: newValue
};
});
}, [quiz]);
return (
<div className="App">
<div>
{quiz.questions?.map((question, index) => (
<div key={index}>
<p>
{question.question} - {question.value}
<button onClick={() => setSelectedQuestionIndex(index)}>
select
</button>
</p>
<br />
</div>
))}
</div>
<br />
<small>change value for question #{selectedQuestionIndex}</small>
{selectedQuestionIndex !== undefined ? (
<div>
<input
type="number"
id="Value"
name="Value"
required="required"
className="edit-input shadow"
min="0"
value={quiz.questions[selectedQuestionIndex].value}
onChange={handleUpdateQuestionScore}
/>
<button onClick={() => setSelectedQuestionIndex(undefined)}>
remove
</button>
</div>
) : (
<div>nothing selected</div>
)}
<h1>current total score = {quiz.value}</h1>
</div>
);
}
Here is a bit more clear way of handling "quiz.value" using useMemo:
import "./styles.css";
import { useMemo, useState } from "react";
export default function App() {
const [quiz, setQuiz] = useState({
title: "",
number: 6,
questions: [
{ question: "q1", value: 0 },
{ question: "q2", value: 0 }
]
});
const [selectedQuestionIndex, setSelectedQuestionIndex] = useState(undefined);
const selectedQuestion = useMemo(() => {
if (!quiz?.questions || selectedQuestionIndex === undefined)
return undefined;
return quiz.questions[selectedQuestionIndex];
}, [quiz, selectedQuestionIndex]);
const quizTotalValue = useMemo(() => {
if (!quiz || !quiz.questions) return 0;
return quiz.questions.reduce((acc, curr) => acc + curr.value, 0);
}, [quiz]);
const handleUpdateQuestionScore = (e) => {
if (!selectedQuestion) return;
selectedQuestion.value = parseInt(e.target.value, 10);
setQuiz((prev) => ({ ...prev }));
};
return (
<div className="App">
<div>
{quiz.questions?.map((question, index) => (
<div key={index}>
<p>
{question.question} - {question.value}
<button onClick={() => setSelectedQuestionIndex(index)}>
select
</button>
</p>
<br />
</div>
))}
</div>
<br />
<small>change value for question #{selectedQuestionIndex}</small>
{selectedQuestion !== undefined ? (
<div>
<input
type="number"
id="Value"
name="Value"
required="required"
className="edit-input shadow"
min="0"
value={selectedQuestion.value}
onChange={handleUpdateQuestionScore}
/>
<button onClick={() => setSelectedQuestionIndex(undefined)}>
remove
</button>
</div>
) : (
<div>nothing selected</div>
)}
<h1>current total score = {quizTotalValue}</h1>
</div>
);
}

serch and redirect to id in react js

i trying to make a history.push on button click
i have this search bar that will show the names of doctors when serched {suggestion.firstname}
i am trying to pass {suggestion.id } as url when cliked on the li corresponding
but here when i type and if the {suggestion.firstname} first letter comes then it automaticaly is redirecting when typing in the input field.
finddoctor is working like onchange funtion but i have written onclick funtion
function finddoctor(e) {
console.log(e);
history.push(`/detiled/${e} `);
}
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<>
<li
style={styles.listyle}
onClick={finddoctor(suggestion.id)}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("admin-panel/all-doctors-list/")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
change it do this, and it should work.
<li
style={styles.listyle}
onClick={() => finddoctor(suggestion.id)}
key={index}
>
{suggestion.firstname}
</li>

not able to redirect in react js using history.push

i trying to make a history.push on button click
i have this search bar that will show the names of doctors when serched {suggestion.firstname}
i am trying to pass {suggestion.id } as url when cliked on the li corresponding
when clicked on li no call going to finddoctor
function finddoctor(e) {
console.log(e);
history.push(`/detiled/${e} `);
}
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<li
style={styles.listyle}
onClick={()=> finddoctor(suggestion.id)}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("admin-panel/all-doctors-list/")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
Are you using "react-router-dom" in your project?
In this case, you should use the history object in a specific way. For example, you can get it with the useHistory hook.
import { useHistory } from "react-router-dom";
const SuggestionsList = ({
suggestions,
inputValue,
displaySuggestions,
selectedSuggestion,
}) => {
let history = useHistory();
const finddoctor = (e) => {
console.log(e);
history.push(`/detiled/${e} `)
};
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list">
{suggestions.map((suggestion, index) => {
return (
<li
onClick={() => finddoctor(suggestion.id)}
key={index}
>
{suggestion.firstname}
</li>
)
})}
</ul>
)
} else {
return <div>No suggestions available...</div>
}
}
return <></>
};

Why the wrong element is being updated only when uploading files?

I have built a component CreatePost which is used for creating or editing posts,
the problem is if I render this component twice even if I upload a file from the second component they are changed in the first one, why? Here is the code:
import FileUpload from "#components/form/FileUpload";
import { Attachment, Camera, Video, Writing } from "public/static/icons";
import styles from "#styles/components/Post/CreatePost.module.scss";
import { useSelector } from "react-redux";
import { useInput, useToggle } from "hooks";
import { useRef, useState } from "react";
import StyledButton from "#components/buttons/StyledButton";
import Modal from "#components/Modal";
import { post as postType } from "types/Post";
import Removeable from "#components/Removeable";
interface createPostProps {
submitHandler: (...args) => void;
post?: postType;
isEdit?: boolean;
}
const CreatePost: React.FC<createPostProps> = ({ submitHandler, post = null, isEdit = false }) => {
console.log(post);
const maxFiles = 10;
const [showModal, setShowModal, ref] = useToggle();
const [description, setDescription] = useInput(post?.description || "");
const user = useSelector((state) => state.user);
const [files, setFiles] = useState<any[]>(post?.files || []);
const handleFileUpload = (e) => {
const fileList = Array.from(e.target.files);
if (fileList.length > maxFiles || files.length + fileList.length > maxFiles) {
setShowModal(true);
} else {
const clonedFiles = [...files, ...fileList];
setFiles(clonedFiles);
}
e.target.value = "";
};
const removeHandler = (id) => {
const filtered = files.filter((file) => file.name !== id);
setFiles(filtered);
};
return (
<div className={styles.createPost}>
<div className={styles.top}>
<span>
<img src="/static/images/person1.jpg" />
</span>
<textarea
onChange={setDescription}
className="primaryScrollbar"
aria-multiline={true}
value={description}
placeholder={`What's on your mind ${user?.name?.split(" ")[0]}`}
></textarea>
{description || files.length ? (
<StyledButton
background="bgPrimary"
size="md"
className={styles.submitButton}
onClick={() => {
if (!isEdit)
submitHandler({
files: files,
author: { name: user.name, username: user.username },
postedTime: 52345,
id: Math.random() * Math.random() * 123456789101112,
comments: [],
likes: [],
description,
});
else {
submitHandler({
...post,
description,
files,
});
}
setDescription("");
setFiles([]);
}}
>
{isEdit ? "Edit" : "Post"}
</StyledButton>
) : null}
</div>
<div className={styles.middle}>
<div className={styles.row}>
{files.map((file) => {
return (
<Removeable
key={file.name + Math.random() * 100000}
removeHandler={() => {
removeHandler(file.name);
}}
>
{file.type.includes("image") ? (
<img src={URL.createObjectURL(file)} width={150} height={150} />
) : (
<video>
<source src={URL.createObjectURL(file)} type={file.type} />
</video>
)}
</Removeable>
);
})}
</div>
</div>
<div className={styles.bottom}>
<FileUpload
id="uploadPhoto"
label="upload photo"
icon={
<span>
<Camera /> Photo
</span>
}
className={styles.fileUpload}
multiple
onChange={handleFileUpload}
accept="image/*"
/>
<FileUpload
id="uploadVideo"
label="upload video"
icon={
<span>
<Video /> Video
</span>
}
className={styles.fileUpload}
multiple
onChange={handleFileUpload}
accept="video/*"
/>
<FileUpload
id="writeArticle"
label="write article"
icon={
<span>
<Writing /> Article
</span>
}
className={styles.fileUpload}
multiple
onChange={handleFileUpload}
/>
</div>
{showModal && (
<Modal size="sm" backdrop="transparent" ref={ref} closeModal={setShowModal.bind(null, false)} yPosition="top">
<p>Please choose a maximum of {maxFiles} files</p>
<StyledButton size="md" background="bgPrimary" onClick={setShowModal.bind(null, false)}>
Ok
</StyledButton>
</Modal>
)}
</div>
);
};
export default CreatePost;
Now on my main file I have:
const Main = () => {
const [posts, setPosts] = useState<postType[]>([]);
const addPost = (post: postType) => {
setPosts([post, ...posts]);
};
const editPost = (post: postType) => {
const updated = posts.map((p) => {
if (post.id === post.id) {
p = post;
}
return p;
});
setPosts(updated);
};
const deletePost = (id) => {
const filtered = posts.filter((post) => post.id !== id);
setPosts(filtered);
};
return (
<>
<CreatePost submitHandler={addPost} key="0" />
<CreatePost submitHandler={addPost} key="1"/>
{posts.map((post) => {
return <PostItem {...post} editHandler={editPost} key={post.id} deleteHandler={deletePost.bind(null, post.id)} />;
})}
</>
);
};
export default Main;
I tried to add/remove the key but doesn't change anything, also tried to recreate this problem in a simpler way in sandbox but I can't it works fine there. And the problem is only when I upload files not when I write text inside the <textarea/>
Note: The second in reality is shown dynamically inside a modal when clicked edit in a post, but I just showed it here for simplicity because the same problem occurs in both cases.
Okay after some hours of debugging I finally found the problem.
Because my <FileUpload/> uses id to target the input inside the <CreatePost/> the <FileUpload/> always had same it, so when I used <CreatePost/> more than 1 time it would target the first element that found with that id that's why the first component was being updated

How can I edit a todo in my react app using hooks?

I'm trying to figure out how to edit a todo item in my react app using hooks, but I can't seem to figure out how to write the code.
Most of the solutions I've seen online are using class components and it's not written with the same logic as my app.
Here is my current code
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = todo => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = id => {
const removedArr = [...todos].filter(todoId => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = id => {
let updatedTodos = todos.map(todo => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
const editTodo = e => {
setTodos(e.target.value);
};
return (
<>
<TodoForm onSubmit={addTodo} />
{todos.map(todo => (
<div>
<div
key={todo.id}
className={todo.isComplete ? 'complete' : ''}
key={todo.id}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
</div>
))}
</>
);
}
Here is the code from the other component
function TodoForm(props) {
const [input, setInput] = useState('');
const handleChange = e => {
setInput(e.target.value);
};
const handleSubmit = e => {
e.preventDefault();
props.onSubmit({
id: Math.floor(Math.random() * 10000),
text: input,
complete: false
});
setInput('');
};
return (
<form onSubmit={handleSubmit}>
<input
placeholder='todo...'
value={input}
onChange={handleChange}
name='text'
/>
<button onClick={handleSubmit}>add todo</button>
</form>
);
}
So right now everything works where I can add todos and delete todos + cross out todos. Only thing missing is being able to edit them.
I saw some suggestions about updating the text value with an input form, but I'm not too sure how I'd implement that in my editTodo function.
Similar to your removeTodo handler, you want to pass the todo.id to completeTodo.
<div className={todo.isComplete ? "complete" : ""} key={todo.id} onClick={() => completeTodo(todo.id)}>
Then you would update a bool value in the todo object.
const completeTodo = (id) => {
let updatedTodos = todos.map(todo => {
if(todo.id === id){
todo.isComplete = true
}
return todo
})
setTodos(updatedTodos)
};
Edit: add styling strikethrough
You'll then conditionally add a css style based on isComplete boolean
CSS
.complete {
text-decoration: line-through;
}
To be able to click on the Remove button, place it outside the todo div in your map function.
{todos.map((todo, isComplete) => (
<>
<div
key={todo.id}
onClick={completeTodo}
className={isComplete ? 'complete' : ''}
>
{todo.text}
</div>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
</>
))}
As discussion with you in another question here it is:
TodoList.js
import React, { useState } from "react";
import TodoForm from "./TodoForm";
import Todo from "./Todo";
function TodoList({ onClick }) {
const [todos, setTodos] = useState([]);
//Track is edit clicked or not
const [editId, setEdit] = useState(false);
//Save input value in input box
const [inputValue, setInputValue] = useState("");
const handleEditChange = (id, text) => {
setEdit(id);
setInputValue(text);
};
const addTodo = (todo) => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = (id) => {
const removedArr = [...todos].filter((todoId) => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = (id) => {
let updatedTodos = todos.map((todo) => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
const editTodo = (id, text) => {
let editTodos = todos.map((todo) => {
if (todo.id === id) {
todo.text = text;
}
return todo;
});
setTodos(editTodos);
setEdit(false);
};
return (
<>
<TodoForm onSubmit={addTodo} />
{/* I want to move this code below into a new component called Todo.js */}
<Todo
todos={todos}
completeTodo={completeTodo}
removeTodo={removeTodo}
editTodo={editTodo}
handleEditChange={handleEditChange}
editId={editId}
inputValue={inputValue}
setInputValue={setInputValue}
/>
</>
);
}
export default TodoList;
Todo.js
// I want to move this code into this component
import React, { useState } from "react";
import { FaWindowClose, FaRegEdit } from "react-icons/fa";
const Todo = ({
todos,
completeTodo,
removeTodo,
editTodo,
editId,
handleEditChange,
inputValue,
setInputValue
}) => {
return todos.map((todo) => (
<div className="todo-row">
{editId === todo.id ? (
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
) : (
<div
key={todo.id}
className={todo.isComplete ? "complete" : ""}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
)}
{editId === todo.id ? (
<button onClick={() => editTodo(todo.id, inputValue)}>Edit todo</button>
) : (
<>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
<FaRegEdit onClick={() => handleEditChange(todo.id, todo.text)} />
</>
)}
</div>
));
};
export default Todo;
Make sure you read and understand code first. Logic is pretty simple what you do in completeTodo. You just need to update text part. Tricky part is to open in input. So logic is like track if user click on id set that id. And check if id is there open input with that id value other wise normal one.
Here is demo of this POC: https://codesandbox.io/s/nostalgic-silence-idm21?file=/src/Todo.js:0-1059

Resources