How to enable a button with useRef() in reactJs - reactjs

I created a Todo-list app in react
and inside each item placed one radio button and one edit, delete button for each and every item when a user enters in the input text.
Basically, the edit, delete buttons are disabled at first.
when a user clicks on the radio button then the particular item buttons get enabled.
But here I am getting all the buttons in each item gets enabled inside the list
import React,{useState} from 'react';
import Modal from 'react-modal';
import {AiFillDelete,AiFillEdit} from 'react-icons/ai';
import './App.css';
Modal.setAppElement('#root')
function App() {
const [todos,setTodos] = useState([{id:0,text:"item1"},{id:1,text:"item2"}])
const [todo,setTodo] = useState("")
const [todoEditing,setTodoEditing] = useState(null)
const [editingText,setEditingText] = useState("")
const [deleteItem,setDeleteItem] = useState()
const [editItem,setEditItem]=useState()
const [modalIsOpen,setModalIsOpen] = useState()
function handleSubmit(e){
e.preventDefault()
const newTodo = {
id:todos.length,
text : todo,
}
setTodos([newTodo].concat(...todos))
setTodo("")
}
function deleteTodo(id){
const updateTodos = [...todos].filter((todo)=>todo.id !== id)
setTodos(updateTodos)
}
function editTodo(id){
const updateTodos = [...todos].map((todo) => {
if(todo.id===id){
todo.text = editingText
}
return todo
})
setTodos(updateTodos)
setTodoEditing(null)
setEditingText("")
}
const handleRadioBtnItem = (event) =>{
setDeleteItem(event.target.value);
setEditItem(event.target.value);
}
return (
<div className="App">
<div className='todo-head'>
<h1 className='ForHeading'>Todo List</h1>
<form onSubmit={handleSubmit}>
<input className='User-Input' type='text' onChange={(e)=>setTodo(e.target.value)} value={todo}/>
<button className='Add-Btn' type='submit' disabled={!todo}>Add Todo</button>
</form>
</div>
{todos.map((todo)=>
<ul className='ul-Style' key={todo.id} id={todo.id}>
<input className='Rdo' type='radio' onClick={handleRadioBtnItem}/>
{todoEditing === todo.id ?
(
<div>
<Modal
isOpen={modalIsOpen}
shouldCloseOnOverlayClick={false}
style={
{
overlay:{
backgroundColor:'gray'
},
content:{
textAlign:'center'
}
}
}
>
<h2>Edit Items</h2>
<input type='text' onChange={(e)=> setEditingText(e.target.value)} value={editingText}/>
<div>
<button onClick={()=>editTodo(todo.id)} disabled=''>Save</button>
<button onClick={()=>setModalIsOpen(false)}>Close</button>
</div>
</Modal>
{todo.text}
</div>
)
:
(
<p>{todo.text}</p>
)
}
<button
className='Edit-Btn'
onClick={()=>{setTodoEditing(todo.id);setModalIsOpen(true)}}
disabled={!editItem}><AiFillEdit/>
</button>
<button
className='Del-Btn'
onClick={()=>deleteTodo(todo.id)}
disabled={!deleteItem}><AiFillDelete/>
</button>
</ul>)}
</div>
);
}
export default App;

Related

Cant display todo items in simple React app

i am new to React and i want to build a simple todo app.
I have made two components one to make a todo item and one for display it.
import { useRef, useState } from "react";
import TodoList from "./TodoList";
function TodoForm() {
const inputItem = useRef();
const [itemExist, setItem] = useState(false);
function submitHandler(event) {
event.preventDefault();
setItem(true);
}
return (
<div>
<form onSubmit={submitHandler}>
<label>
Todo-item
<input name="item" type="text" ref={inputItem}></input>
</label>
<input type="submit"></input>
</form>
{itemExist && <TodoList item={inputItem.current.value}></TodoList>}
</div>
);
}
export default TodoForm;
Component for display the doto items
function TodoList(props) {
return (
<ul>
<li>{props.item }</li>
</ul>
);
}
export default TodoList;
When i submit the first item i cant add other item.
I have tried to to change the itemExist to false under the
{itemExist && <TodoList item={inputItem.current.value}></TodoList>}
but error occurs. Uncaught Error: Too many re-renders
function TodoForm() {
const inputItem = useRef();
const [todos, setTodos] = useState([]);
function submitHandler(event) {
event.preventDefault();
let newTodos = [...todos, { inputItem.current.value }];
setTodos(newTodos);
}
return (
<div>
<form onSubmit={submitHandler}>
<label>
Todo-item
<input name="item" type="text" ref={inputItem}></input>
</label>
<input type="submit"></input>
</form>
{todos.map((todo, i) => {
return(
<TodoList key={`todo_${i}`} item={todo}></TodoList>
)
})}
</div>
);
}

Dynamic react toggle button for multiple elements

I need to toggle multiple blocks with true/false in react state, but true and false works for all blocks at the same time.
import { useState } from "react";
...
const [toggleThisElement, setToggleThisElement] = useState(false);
...
{
data.map((d, id) => {
return (
<div className="single-history" key={id}>
<div className="h-head">
click this div for toggle h-info block
</div>
<div className="h-info">toggling block</div>
</div>
)
})
}
Currently, all your toggle items share the same state. To avoid that create a separate component for toggling logic called ToggleItem as below.
import { useState } from "react";
const ToggleItem = ({ discription, id }) => {
const [toggleThisElement, setToggleThisElement] = useState(false);
return (
<div className="single-history" key={id}>
<button
className="h-head"
onClick={() => setToggleThisElement((prev) => !prev)}
>
click this btn for toggle h-info block {id}
</button>
{toggleThisElement && <div className="h-info">{discription}</div>}
</div>
);
};
export default function App() {
const data = ["first", "second", "third"];
return (
<>
{data.map((d, id) => {
return <ToggleItem id={id} discription={d} />;
})}
</>
);
}

Button 'more' in a popup box doesn’t work right with React hook useState

I created a component (sort of popup box) which displays a sign of horoscope, there’s an image and description. The popup box works correctly. I added a button ‘more’ to see more description, so I used a useState for it, but it doesn’t work, when I click on it doesn't show the rest of the text.
Thanks for your help !
const Modal = ({
children, visible, hide, fermer, more,
}) => {
const popup = `popup ${visible ? 'block' : 'hidden'}`;
return (
<div className={popup}>
{fermer ? null : (
<button className="close" onClick={hide} type="button">X</button>
)}
{children}
<button className="more" onClick={more} type="button">more</button>
</div>
);
};
export default Modal;
import './App.css';
import { useState } from 'react';
import Element from './Element';
import Modal from './Modal';
import Bd from './Bd';
function App() {
const bd = Bd.map((element) => (
<Element
nom={element.nom}
image={element.image}
description={element.description}
modulo={element.modulo}
/>
));
const [year, setYear] = useState('');
function handleChange(event) {
setYear(event.target.value);
}
const [signe, setSigne] = useState([]);
const [vis, setVis] = useState(false);
const [desc, setDesc] = useState(true);
function handleSubmit() {
setVis(true);
const yearModulo = Number(year) % 12;
Bd.map((element) => (
yearModulo === element.modulo ? setSigne(
[<h1>{element.nom}</h1>,
<div>{element.description.substr(0, 150)}</div>,
desc ? <div />
: <div>{element.description.substr(150, 600)}</div>,
<img src={`/images/${element.image}`} alt="" />,
],
) : false
));
}
return (
<div>
<div>
<input
className="text-center font-bold"
type="number"
id="year"
name="year"
value={year}
onChange={handleChange}
/>
<button type="submit" onClick={handleSubmit}>Valider</button>
</div>
<div className="flex flex-wrap">{bd}</div>
<Modal
visible={vis}
hide={() => setVis(false)}
more={() => setDesc(false)}
>
<div>
<div>{signe}</div>
</div>
</Modal>
</div>
);
}
export default App;
I would avoid storing in a local state a component (setSigne([<h1>{element.nom}</h1>,...). Prefer storing in the state the values that cannot be computed from other existing states, and generate the elements at rendering.
const [signe, setSigne] = useState(null);
function handleSubmit() {
setVis(true);
const yearModulo = Number(year) % 12;
setSigne(Bd.find(element => yearModulo === element.modulo));
}
// ...
<div>
{signe && <div>
<h1>{signe.nom}</h1>
<div>{signe.description.substr(0, 150)}</div>
{desc ? <div /> : <div>{signe.description.substr(150, 600)}</div>}
<img src={`/images/${signe.image}`} alt="" />
</div>}
</div>
Also, don’t forget to add a key prop when generating elements from an array:
const bd = Bd.map(element => (
<Element
key={element.nom}
// ...

Pass props to component: "Objects are not valid as a React child"

I'm trying to simply pass the Id of a clicked item to display on another page under a different component ("Cart") . At the bottom of the code below, I have a button containing <Cart test={product.id} /> which extracts the Id that I want to be displayed in "Cart" when the button is clicked.
However, I am instead getting an error message of:
Objects are not valid as a React child (found: object with keys
{history, location, match, staticContext}). If you meant to render a
collection of children, use an array instead.
Is there a simple syntax error?
import React, { useState, useEffect, Cart } from 'react';
import './../App.css';
import * as ReactBootStrap from 'react-bootstrap';
function Item(props) {
const [product, setProduct] = useState([]);
const [loading, setLoading] = useState(false);
const [quantity, setQuantity] = useState(1);
const [cost, setCost] = useState([]);
useEffect(async () => {
fetchItems();
}, []);
const itemId = props.match.params.item;
const fetchItems = async () => {
const data = await fetch('https://fakestoreapi.com/products/' + itemId);
const items = await data.json();
setProduct(items);
setLoading(true);
setCost(items.price);
};
function priceUSD(change) {
return change.toFixed(2);
}
useEffect(() => {
const newCost = quantity * product.price;
setCost(priceUSD(newCost));
}, [quantity]);
return (
<div className="App">
<h2>Item</h2>
<div className="gridContainer">
{loading ? (
<div key={itemId} className="productStyle">
<img src={product.image} className="productImage"></img>
<p>{product.title}</p>
<p>{product.description}}</p>
<p>${priceUSD(product.price)}</p>
<div className="quantity">
<button
className="btn minus-btn"
type="button"
onClick={quantity > 1 ? () => setQuantity(quantity - 1) : null}
>
-
</button>
<input type="text" id="quantity" value={quantity} />
<button className="btn plus-btn" type="button" onClick={() => setQuantity(quantity + 1)}>
+
</button>
</div>
<button type="button" onClick={() => <Cart test={product.id} />}>
Add to shopping cart ${cost}
</button>
</div>
) : (
<ReactBootStrap.Spinner className="spinner" animation="border" />
)}
</div>
</div>
);
}
export default Item;
Cart
import React, { useState, Item } from 'react';
import './../App.css';
import './Item.js';
function Cart(test) {
return (
<div className="App">
<p>{test}</p>
</div>
);
}
export default Cart;
Component props are objects. You can read more about them in the official documentation.
You can either destructure the props of the Cart component:
function Cart({test}) {
return (
<div className="App">
<p>{test}</p>
</div>
);
}
or use explicitly test property of props:
function Cart(props) {
return (
<div className="App">
<p>{props.test}</p>
</div>
);
}

Why does React.createPortal doesn't process event.stopPropagation?

I'm experimenting on a custom modal using React.createPortal, everything's working if I am only opening and closing the modal via buttons inside the modal and the default result is displayed on the component below but what I want to achieve is when I click the modal container (dark overlay div) it should close the modal, so I added an exithandler on it, but the problem is the default form button behavior inside the modal content doesn't work anymore even if there's an event.stopPropagation() function in the exithandler.
Here's the codesandbox sample of two modals, one with a form and one without: https://codesandbox.io/s/react-modals-2dnei
Modal container:
import React from "react";
import { createPortal } from "react-dom";
const modalRoot = document.getElementById("modal-root");
function Modal({ exitHandler, children }) {
return createPortal(
<div className="modal" onClick={exitHandler}>
{children}
</div>,
modalRoot
);
}
export default Modal;
Modal content 1:
import React from "react";
const HelloWorld = ({ user, onClose }) => {
return (
<div className="modal-content">
<div className="modal-header">
<div className="modal-title">
<h2>Greetings</h2>
</div>
<div className="modal-action">
<button type="button" onClick={onClose}>X</button>
</div>
</div>
<div className="modal-body">
<p>{user ? `Hello ${user}!` : "Greetings!"}</p>
</div>
</div>
);
};
export default HelloWorld;
Modal Content 2:
import React, { forwardRef, useEffect } from "react";
const SignUp = forwardRef((props, ref) => {
const { onClose, onSignUp, handleChange, inputError, user } = props;
useEffect(() => {
ref.current.focus();
});
return (
<div className="modal-content">
<div className="modal-header">
<div className="modal-title">
<h2>Sign Up</h2>
</div>
<div className="modal-action">
<button type="button" onClick={onClose}>X</button>
</div>
</div>
<div className="modal-body">
<form onSubmit={onSignUp} className="modal-form">
<label className="input-label">
<span>Name:</span>
<input
type="text"
onChange={handleChange}
value={user}
ref={ref}
className={inputError === true ? "input-error" : null}
/>
</label>
<input className="btn" type="submit" value="Submit" />
</form>
</div>
</div>
);
});
export default SignUp;
Main:
const App = () => {
const [modalState, setModalState] = useState(false);
const [modalId, setModalId] = useState(null);
const [inputError, setInputError] = useState(false);
const [user, setUser] = useState("");
const [userStatus, setUserStatus] = useState("");
const nameRef = useRef(null);
const handleShowModal = (e) => {
const modalId = e.target.id.toString();
return [setModalState(true), setModalId(modalId)];
};
const handleHideModal = (event) => {
event.stopPropagation(); // This doesn't work
setModalState(false);
};
const runSignUpResult = useCallback(() => {
handleHideModal();
setUserStatus(`Thank you ${user} for signing up!`);
}, [user]);
const handleValidation = useCallback(
(nameParameter) => {
if (nameParameter.length === 0) {
nameRef.current.focus();
setInputError(true);
} else {
return [setInputError(false), runSignUpResult()];
}
},
[runSignUpResult]
);
const handleSignUp = useCallback(
(e) => {
e.preventDefault();
const name = nameRef.current.value;
handleValidation(name);
},
[nameRef, handleValidation]
);
const handleChange = (e) => {
setUser(e.target.value);
};
const modal = modalState ? (
<Modal exitHandler={handleHideModal}>
{modalId === "greeting" ? (
<HelloWorld onClose={handleHideModal} user={user} />
) : (
<SignUp
onClose={handleHideModal}
onSignUp={handleSignUp}
handleChange={handleChange}
user={user}
ref={nameRef}
inputError={inputError}
modalId={modalId}
/>
)}
</Modal>
) : null;
return (
<div className="App">
<AppHeader />
<main className="app-body">
<section className="modal-btns">
<button id="greeting" className="btn" onClick={handleShowModal}>
Greetings!
</button>
<button id="signup" className="btn" onClick={handleShowModal}>
Sign Up
</button>
</section>
<section>{modal}</section>
<section className="user-status">
<h3>{user.length === 0 ? null : userStatus}</h3>
</section>
</main>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Resources