I am looking for feedback regarding this React code I am writing. I previously posted when I first started out on this and I have since iterated on it and tried to implement best practices. I am learning a lot and I cannot help, but feel that my current code is clunky. In what ways can I improve the design?
I am having a hard time figuring out what needs to be a component, what needs to be a function, and what can just be a value (even then whether it should be state). I also feel like I am misusing variable scope, but I can't put my finger on how. One specific thing I cannot resolve is ESlint is highlighting mostly everything in my file with this error "Function component is not a function declaration". Any and all feedback is appreciated. Thank you!
/* eslint-disable react/prop-types */
/* eslint-disable no-underscore-dangle */
import axios from 'axios';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { GetWishes, InsertWish } from './apis';
// Main component, acts a wrapper for the entire screen content
const Wishlist = () => {
const [loading, setLoading] = useState('initial');
const [listOfWishes, setListOfWishes] = useState('default');
// Passed down to update the main list state
function updateListOfWishes(list) {
setListOfWishes([...list]);
}
// Sorting lists dynamically
function dynamicSort(property) {
let sortOrder = 1;
let newprop = '';
if (property[0] === '-') {
sortOrder = -1;
newprop = property.substr(1);
}
return (a, b) => {
let result = 0;
if ((a[newprop] < b[newprop])) {
result = -1;
} else if (a[newprop] > b[newprop]) {
result = 1;
}
return result * sortOrder;
};
}
// Get all items from DB, this is main list
async function GetWishesList() {
try {
const apiresp = await GetWishes();
apiresp.sort(dynamicSort('-source'));
const goodlist = apiresp.map((item) => ({ ...item, isReadOnly: true, show: true }));
setListOfWishes(goodlist);
setLoading('false');
} catch (e) {
console.log(`Error in Wishlist.GetWishesList: ${e.message}`);
}
}
// Only once, get items and set loading state
useEffect(() => {
setLoading('true');
GetWishesList();
}, []);
if (loading === 'initial') {
return <h2 className="content">Initializing...</h2>;
}
if (loading === 'true') {
return <h2 className="content">Loading...</h2>;
}
// Return header and content, pass down function for deep state update
return (
<div className="contentwrapper">
<WishlistHeader fullList={listOfWishes} updateListOfWishes={updateListOfWishes} />
<WishTable fullList={listOfWishes} updateListOfWishes={updateListOfWishes} />
</div>
);
}
// Header component
const WishlistHeader = (props) => {
const [filter, setFilter] = useState('all');
let list = props.fullList;
// Get length of current filtered list
function getShowCount(list) {
return list.filter((item) => item.show === true).length;
}
// Update shown list items when filter changes
const HandleFilterChange = (e) => {
const { fullList, updateListOfWishes } = props;
const { value } = e.target;
for (let i = fullList.length - 1; i >= 0; i -= 1) {
fullList[i].isReadOnly = true;
fullList[i].show = true;
if (value !== 'all' && fullList[i].category !== value) {
fullList[i].show = false;
}
}
setFilter(value);
const newlist = fullList.map(i => {
return { ...i };
});
updateListOfWishes(newlist);
}
// Return header component content
return (
<div className="contentBanner">
<h1 className="wishTitle">
Wishes:
{' '}
{getShowCount(list)}
</h1>
<label htmlFor="category">
<p className="bannerFilter">Category</p>
<select id="category" name="category" value={filter} onChange={(e) => HandleFilterChange(e)}>
<option value="all">All</option>
<option value="default">Default</option>
<option value="camping">Camping</option>
<option value="hendrix">Hendrix</option>
<option value="decor">Decor</option>
</select>
</label>
</div>
);
}
// Component to show list of items
function WishTable(props) {
const rows = [];
let { fullList, updateListOfWishes } = props;
if (fullList === null) {
console.log('currentList is null');
} else {
fullList.forEach(function (item) {
rows.push(
<div key={item._id} >
<WishRow item={item} currentList={fullList} updateListOfWishes={updateListOfWishes} />
</div>)
})
}
return (
<div className="content">
{rows}
</div>
);
};
// Individual row render for each item
const WishRow = (props) => {
let item = props.item;
let prevItem = useRef(item);
// Store unedited item in case of cancel, mark not read only
const handleEdit = () => {
let { item, currentList, updateListOfWishes } = props;
prevItem.current = { ...item };
const newlist = currentList.map(i => {
if (i._id === item._id) {
return { ...i, isReadOnly: false }
}
return { ...i };
});
updateListOfWishes(newlist);
};
// Send item to DB
async function insertWish(item) {
try {
await InsertWish(item);
} catch (e) {
console.log(`Error in wishlist.insertWish: ${e.message}`);
}
};
// Send current item info to DB and mark read only
const handleSubmit = () => {
let { item, currentList, updateListOfWishes } = props;
insertWish(item);
const newlist = currentList.map(i => {
if (i._id === item._id) {
return { ...i, isReadOnly: true };
}
return { ...i };
});
updateListOfWishes(newlist);
};
// Return content for submit button
function Submit(item) {
if (!item.isReadOnly) {
return (
<span>
<button className="typicalbutton" type="button" onClick={() => handleSubmit(item)}>
Submit
</button>
</span>
);
}
return null;
};
// Return content for edit button
function ShowEdit(item) {
if (item.source === 'manual') {
return (
<span >
<button className="typicalbutton righthand" type="button" onClick={() => handleEdit(item, props.currentList)}>
Edit
</button>
</span>
);
}
return null;
};
// Revert to unedited item and mark read only
const handleCancel = () => {
let { item, currentList, updateListOfWishes } = props;
const newlist = currentList.map(i => {
if (i._id === item._id) {
return { ...prevItem.current, isReadOnly: true }
}
return { ...i };
});
updateListOfWishes(newlist);
};
// Return content for cancel button
function Cancel(item) {
if (!item.isReadOnly) {
return (
<span>
<button className="typicalbutton" type="button" onClick={(e) => handleCancel(e, prevItem, item)}>
Cancel
</button>
</span>
);
}
return null;
};
// Update item when fields edited
const handleChange = (e) => {
let { item, currentList, updateListOfWishes } = props;
const { name, value } = e.target;
item[name] = value;
const newlist = currentList.map(i => {
return { ...i };
});
updateListOfWishes(newlist);
};
// Update item for category change
const handleCategoryChange = (e) => {
const { value } = e.target;
let { item, currentList, updateListOfWishes } = props;
item.category = value;
const newlist = currentList.map(i => {
return { ...i };
});
updateListOfWishes(newlist);
};
// Open url in new tab
const openInTab = (url) => {
const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
if (newWindow) newWindow.opener = null;
};
// Get the url from backend
async function getUrl(link) {
const toflask = `/go_outside_flask/${link}`;
const tempurl = await axios.get(toflask);
const newurl = tempurl.data;
return newurl;
}
// Get the url from backend and go to it in new tab
const goToLink = async (link) => {
let taburl = '';
try {
taburl = await getUrl(link);
return openInTab(taburl);
} catch (e) {
console.log(`Error getting url from link: ${link} ${e.message}`);
return window.location.href;
}
};
// Row content, if read only show just fields, if not read only then show different buttons and editable fields
return (
<div >
{item.show ? (
<div className="wish">
<div className="wishatt">
{
item.isReadOnly ? (
<div>
<span className="wishatt capital">
Category:
{item.category}
</span>
{ShowEdit(item)}
<div className="wishatt">
Item Name:
{item.name}
</div>
<div className="wishatt">
Description:
{item.description}
</div>
<div className="wishatt">
Cost:
{item.cost}
</div>
<span>Link: </span>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a className="wishatt" href="#" onClick={(e) => goToLink(item.link)}>{item.link}</a>
<div className="wishatt">
Quantity:
{item.quantity}
</div>
</div>
)
: (
<span>
<label htmlFor="category">
Category:
<select name="category" onChange={(e) => handleCategoryChange(e, item)} value={item.category}>
<option value="default">Default</option>
<option value="camping">Camping</option>
<option value="hendrix">Hendrix</option>
<option value="decor">Decor</option>
</select>
</label>
<span className="righthandSection">
{Submit(item)}
{Cancel(item)}
</span>
<div>
<div>
<label htmlFor="name">
Item Name:
</label>
<input className="wishatt" name="name" placeholder="Name" onChange={(e) => handleChange(e, item)} value={item.name} />
</div>
<div>
<label htmlFor="description">
Description:
</label>
<input className="wishatt" name="description" placeholder="Description" onChange={(e) => handleChange(e, item)} value={item.description} />
</div>
<div>
<label htmlFor="cost">
Cost:
</label>
<input className="wishatt" name="cost" placeholder="Cost" onChange={(e) => handleChange(e, item)} value={item.cost} />
</div>
<div>
<label htmlFor="link">
Link:
</label>
<input className="wishatt" name="link" placeholder="Link" onChange={(e) => handleChange(e, item)} value={item.link} />
</div>
<div>
<label htmlFor="quantity">
Quantity:
</label>
<input className="wishatt" name="quantity" placeholder="Quantity" onChange={(e) => handleChange(e, item)} value={item.quantity} />
</div>
<div className="wishatt">
Wishlist:
{item.wishlist}
</div>
</div>
</span>
)
}
</div>
</div>
) : null}
</div>
)
}
export default Wishlist;
Related
I wanted to create a select-all checkbox, each product row has a checkbox, once the checkbox is clicked it will take its product id, variations, and count to calculate and display the total price of the product selected. I wanted to do like when clicking on select all, all the other checks need to tick and display the total of all product prices, I did the code for each row checkbox and it work but I am not sure how to do for select all checkbox, need help on it, below are code, and https://codesandbox.io/s/select-all-checkbox-uzmllg this is codesand box.
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
const [isCheckAll, setIsCheckAll] = useState(false);
const [checkedItems, setCheckedItems] = useState(
JSON.parse(localStorage.getItem("checkedItems") || "[]")
);
useEffect(() => {
localStorage.setItem("checkedItems", JSON.stringify(checkedItems));
}, [checkedItems]);
const addChecked = (itemId, variationId, qty) => {
setCheckedItems([
...checkedItems,
{ ProductID: itemId, VariationID: variationId, Count: qty }
]);
};
const removeChecked = (itemId, variationId) => {
const toBeRemove = checkedItems.find(
(item) => item.ProductID === itemId && item.VariationID === variationId
);
if (toBeRemove) {
checkedItems.splice(checkedItems.indexOf(toBeRemove), 1);
setCheckedItems([...checkedItems]);
}
};
const getCheckedStatus = (itemId, variationId) => {
const found = checkedItems.find(
(item) => item.ProductID === itemId && item.VariationID === variationId
);
return found !== null;
};
const handleSelectAll = (e) => {
if (isCheckAll) {
//
}
};
return (
<div className="App">
{cartData.Result.map((shop) =>
shop.ShopCartList.map((cart) => (
<div key={cart.ShopID} md="12" lg="12">
{cart.productCartList.map((items) => {
return (
<div key={items.VariationID} md="12" lg="12">
<div id="additem" className="pt-5">
{items.Stock === 0 ? (
<h6 className="bg-light text-danger font-weight-bold ">
SOLD OUT
</h6>
) : (
<input
type="checkbox"
value={getCheckedStatus(
items.ProductID,
items.VariationID
)}
onChange={(e) => {
if (e.target.checked) {
addChecked(
items.ProductID,
items.VariationID,
items.Count
);
} else {
removeChecked(
items.ProductID,
items.VariationID,
items.Count
);
}
}}
/>
)}
</div>
</div>
);
})}
</div>
))
)}
<div>
<input
type="checkbox"
name="selectAll"
id="selectAll"
handleClick={handleSelectAll}
isChecked={isCheckAll}
/>
Select All
</div>
</div>
);
}
In handleSelectAll you need to set checkedItems is all your array items. You dont need isCheckAll state, you can see check all status by verify length of your checkedItems
const flattenCartData = (cartData) => {
const arr = [];
cartData.Result.forEach((shop) => {
shop.ShopCartList.forEach((cart) => {
cart.productCartList.forEach((items) => {
arr.push(items);
});
});
});
return arr;
};
export default function App() {
const [checkedItems, setCheckedItems] = useState(
JSON.parse(localStorage.getItem("checkedItems") || "[]")
);
const ITEMS = flattenCartData(cartData);
const isCheckAll = checkedItems.length === ITEMS.length;
useEffect(() => {
localStorage.setItem("checkedItems", JSON.stringify(checkedItems));
}, [checkedItems]);
const addChecked = (itemId, variationId, qty) => {
setCheckedItems([
...checkedItems,
{ ProductID: itemId, VariationID: variationId, Count: qty }
]);
};
const removeChecked = (itemId, variationId) => {
const toBeRemove = checkedItems.find(
(item) => item.ProductID === itemId && item.VariationID === variationId
);
if (toBeRemove) {
checkedItems.splice(checkedItems.indexOf(toBeRemove), 1);
setCheckedItems([...checkedItems]);
}
};
const getCheckedStatus = (itemId, variationId) => {
const found = checkedItems.find(
(item) => item.ProductID === itemId && item.VariationID === variationId
);
return found !== undefined;
};
const handleSelectAll = (e) => {
if (isCheckAll) {
setCheckedItems([]);
} else setCheckedItems([...ITEMS]);
};
return (
<div className="App">
{cartData.Result.map((shop) =>
shop.ShopCartList.map((cart) => (
<div key={cart.ShopID} md="12" lg="12">
{cart.productCartList.map((items) => {
return (
<div key={items.VariationID} md="12" lg="12">
<div id="additem" className="pt-5">
{items.Stock === 0 ? (
<h6 className="bg-light text-danger font-weight-bold ">
SOLD OUT
</h6>
) : (
<div>
<input
type="checkbox"
checked={getCheckedStatus(
items.ProductID,
items.VariationID
)}
onChange={(e) => {
if (e.target.checked) {
addChecked(
items.ProductID,
items.VariationID,
items.Count
);
} else {
removeChecked(
items.ProductID,
items.VariationID,
items.Count
);
}
}}
/>
<span>{items.ProductName}</span>
</div>
)}
</div>
</div>
);
})}
</div>
))
)}
<div>
<input
type="checkbox"
name="selectAll"
id="selectAll"
onChange={handleSelectAll}
checked={isCheckAll}
/>
Select All
</div>
</div>
);
}
I have created a codesandbox. You can check, hope it help!
I was trying to make a ToDo Site and I wanted to save if the checkbox is checked or not in LocalStorage. But whenever I am checking the button the values inside the last are vanishing and it is not even being saved in the LocalStorage.
The following is my code:
import React from "react";
import Navbar from './components/Navbar'
import '../src/App.css'
export default function TodoInput() {
const saveLocalTasks = () => {
let savedTasks = localStorage.getItem('tasks');
console.log(savedTasks)
if (savedTasks) {
return JSON.parse(localStorage.getItem('tasks'));
} else {
return [];
}
}
const [task, setTask] = React.useState('')
// const [count, setCount] = React.useState(0)
const [taskList, setTaskList] = React.useState(saveLocalTasks)
const [disable, setDisable] = React.useState(true)
const [edit, setEdit] = React.useState(true)
const [isTaskEdit, setIsTaskEdit] = React.useState(null)
React.useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(taskList))
}, [taskList]);
const updateTaskList = () => {
if (task && !edit) {
setTask('')
setTaskList(
taskList.map((item) => {
if(item.key === isTaskEdit) {
return {...item, object: task}
}
setEdit(true)
return item
}
)
)
}else {
setTaskList([...taskList, {object: task, key: Date.now(), completed: false}])
setTask('')
// setCount(count + 1)
setDisable(true)
}
}
const inputValue = e => {
setTask(e.target.value)
e.target.value === '' || task === '' || task.length === 0
?
setDisable(true)
:
setDisable(false)
}
console.log(task.length)
const deleteTaskListItem = (key) => {
const updatedList = taskList.filter((item) => {
return (
item.key !== key
)
})
setTaskList(updatedList)
// setCount(count - 1)
}
const editTask = (key) => {
let newTask = taskList.find((item) => {
return (
item.key === key
)
})
console.log(newTask)
setEdit(false)
setTask(newTask.object)
setIsTaskEdit(key)
}
const boxChecked = (key) => {
let checkedTask = taskList.map((item) => {
if (item.key === key) {
taskList.completed = !taskList.completed
}
return (
task
)
})
setTaskList(checkedTask)
}
return (
<div>
<Navbar />
<header>
<div className="todolist-border">
<div className="todo-input-form">
<input
className = "inputText"
placeholder="Add a Task"
value={task}
onChange = {inputValue}
/>
{
edit
?
<button
disabled = {disable}
onClick = {updateTaskList} className="todo-add-button">
+
</button>
:
<button className="edit-button" onClick={updateTaskList}>
<i className="fas fa-edit"></i>
</button>
}
</div>
<div>
{taskList.map((item) => {
return (
<div key = {item.key} className="todolist-div">
<input type="checkbox" className="list-checkbox" id="completed"
onChange={() => boxChecked(item.key)}
checked = {item.completed}
key = {item.key}
>
</input>
<p>{item.object}</p>
<div>
<button className="edit-button" onClick={() => editTask(item.key)}>
<i className="fas fa-edit"></i>
</button>
<button onClick={()=>deleteTaskListItem(item.key)} className="delete-button">
X
</button>
</div>
</div>
)
})}
</div>
</div>
</header>
</div>
)
}
The following error occurs when I check the checkbox:
And also I whenever I click the edit button, option to edit comes on the imput box and the add button changes to edit button. I put the state setEdit(True) after the edit button click but the following error shows up whenever I click on the edit button:
Kindly check and please correct the errors in the code
The link to the site is:
https://to-do-ist.netlify.app/
The site does not have the above code, but has the bugs I mentioned(edit button and checkbox)
The code for the site is as follows:
import React from "react";
import Navbar from './components/Navbar'
import '../src/App.css'
export default function TodoInput() {
const saveLocalTasks = () => {
let savedTasks = localStorage.getItem('tasks');
console.log(savedTasks)
if (savedTasks) {
return JSON.parse(localStorage.getItem('tasks'));
} else {
return [];
}
}
const [task, setTask] = React.useState('')
const [count, setCount] = React.useState(0)
const [taskList, setTaskList] = React.useState(saveLocalTasks)
const [disable, setDisable] = React.useState(true)
const [edit, setEdit] = React.useState(true)
const [isTaskEdit, setIsTaskEdit] = React.useState(null)
React.useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(taskList))
}, [taskList]);
const updateTaskList = () => {
if (task && !edit) {
setTaskList(
taskList.map((item) => {
if(item.key === isTaskEdit) {
return {...item, object: task}
}
return item
})
)
}else {
setTaskList([...taskList, {object: task, key: Date.now()}])
setTask('')
setCount(count + 1)
setDisable(true)
}
// setEdit(true)
}
const inputValue = e => {
setTask(e.target.value)
e.target.value === '' || task === '' || task.length === 0
?
setDisable(true)
:
setDisable(false)
}
console.log(task.length)
const deleteTaskListItem = (key) => {
const updatedList = taskList.filter((item) => {
return (
item.key !== key
)
})
setTaskList(updatedList)
setCount(count - 1)
}
const editTask = (key) => {
let newTask = taskList.find((item) => {
return (
item.key === key
)
})
console.log(newTask)
setEdit(false)
setTask(newTask.object)
setIsTaskEdit(key)
}
return (
<div>
<Navbar />
<header>
<div className="todolist-border">
<div className="todo-input-form">
<input
className = "inputText"
placeholder="Add a Task"
value={task}
onChange = {inputValue}
/>
{
edit
?
<button
disabled = {disable}
onClick = {updateTaskList} className="todo-add-button">
+
</button>
:
<button className="edit-button" onClick={updateTaskList}>
<i className="fas fa-edit"></i>
</button>
}
</div>
<div>
{taskList.map((item) => {
return (
<div key = {item.key} className="todolist-div">
<input type="checkbox" className="list-checkbox">
</input>
<p>{item.object}</p>
<div>
<button className="edit-button" onClick={() => editTask(item.key)}>
<i className="fas fa-edit"></i>
</button>
<button onClick={()=>deleteTaskListItem(item.key)} className="delete-button">
X
</button>
</div>
</div>
)
})}
</div>
</div>
</header>
</div>
)
}
I am building a clone of the Google Keep app with react js. I added all the basic functionality (expand the create area, add a note, delete it) but I can't seem to manage the edit part. Currently I am able to edit the inputs and store the values in the state, but how can I replace the initial input values for the new values that I type on the input?
This is Note component
export default function Note(props) {
const [editNote, setEditNote] = useState(false);
const [currentNote, setCurrentNote] = useState({
id: props.id,
editTitle: props.title,
editContent: props.content,
});
const handleDelete = () => {
props.deleteNote(props.id);
};
const handleEdit = () => {
setEditNote(true);
setCurrentNote((prevValue) => ({ ...prevValue }));
};
const handleInputEdit = (event) => {
const { name, value } = event.target;
setCurrentNote((prevValue) => ({
...prevValue,
[name]: value,
}));
};
const updateNote = () => {
setCurrentNote((prevValue, id) => {
if (currentNote.id === id) {
props.title = currentNote.editTitle;
props.content = currentNote.editContent;
} else {
return { ...prevValue };
}
});
setEditNote(false);
};
return (
<div>
{editNote ? (
<div className='note'>
<input
type='text'
name='edittitle'
defaultValue={currentNote.editTitle}
onChange={handleInputEdit}
className='edit-input'
/>
<textarea
name='editcontent'
defaultValue={currentNote.editContent}
row='1'
onChange={handleInputEdit}
className='edit-input'
/>
<button onClick={() => setEditNote(false)}>Cancel</button>
<button onClick={updateNote}>Save</button>
</div>
) : (
<div className='note' onDoubleClick={handleEdit}>
<h1>{props.title}</h1>
<p>{props.content}</p>
<button onClick={handleDelete}>DELETE</button>
</div>
)}
</div>
);
}
And this is the Container component where I am renderind the CreateArea and mapping the notes I create. I tried to map the notes again with the new values but it wasn't working.
export default function Container() {
const [notes, setNotes] = useState([]);
const addNote = (newNote) => {
setNotes((prevNotes) => {
return [...prevNotes, newNote];
});
};
const deleteNote = (id) => {
setNotes((prevNotes) => {
return prevNotes.filter((note, index) => {
return index !== id;
});
});
};
// const handleUpdateNote = (id, updatedNote) => {
// const updatedItem = notes.map((note, index) => {
// return index === id ? updatedNote : note;
// });
// setNotes(updatedItem);
// };
return (
<div>
<CreateArea addNote={addNote} />
{notes.map((note, index) => {
return (
<Note
key={index}
id={index}
title={note.title}
content={note.content}
deleteNote={deleteNote}
//handleUpdateNote={handleUpdateNote}
/>
);
})}
</div>
);
}
There are a couple of mistakes in your code.
The state properties are in the camel case
const [currentNote, setCurrentNote] = useState({
...
editTitle: props.title,
editContent: props.content,
});
But the names of the input are in lowercase.
<input
name='edittitle'
...
/>
<textarea
name='editcontent'
...
/>
Thus in handleInputEdit you don't update the state but add new properties: edittitle and editcontent. Change the names to the camel case.
In React you cant assign to the component prop values, they are read-only.
const updateNote = () => {
...
props.title = currentNote.editTitle;
props.content = currentNote.editContent;
You need to use the handleUpdateNote function passed by the parent component instead. You have it commented for some reason.
<Note
...
//handleUpdateNote={handleUpdateNote}
/>
Check the code below. I think it does what you need.
function Note({ id, title, content, handleUpdateNote, deleteNote }) {
const [editNote, setEditNote] = React.useState(false);
const [currentNote, setCurrentNote] = React.useState({
id,
editTitle: title,
editContent: content,
});
const handleDelete = () => {
deleteNote(id);
};
const handleEdit = () => {
setEditNote(true);
setCurrentNote((prevValue) => ({ ...prevValue }));
};
const handleInputEdit = (event) => {
const { name, value } = event.target;
setCurrentNote((prevValue) => ({
...prevValue,
[name]: value,
}));
};
const updateNote = () => {
handleUpdateNote({
id: currentNote.id,
title: currentNote.editTitle,
content: currentNote.editContent
});
setEditNote(false);
};
return (
<div>
{editNote ? (
<div className='note'>
<input
type='text'
name='editTitle'
defaultValue={currentNote.editTitle}
onChange={handleInputEdit}
className='edit-input'
/>
<textarea
name='editContent'
defaultValue={currentNote.editContent}
row='1'
onChange={handleInputEdit}
className='edit-input'
/>
<button onClick={() => setEditNote(false)}>Cancel</button>
<button onClick={updateNote}>Save</button>
</div>
) : (
<div className='note' onDoubleClick={handleEdit}>
<h1>{title}</h1>
<p>{content}</p>
<button onClick={handleDelete}>DELETE</button>
</div>
)}
</div>
);
}
function CreateArea() {
return null;
}
function Container() {
const [notes, setNotes] = React.useState([
{ title: 'Words', content: 'hello, bye' },
{ title: 'Food', content: 'milk, cheese' }
]);
const addNote = (newNote) => {
setNotes((prevNotes) => {
return [...prevNotes, newNote];
});
};
const deleteNote = (id) => {
setNotes((prevNotes) => {
return prevNotes.filter((note, index) => {
return index !== id;
});
});
};
const handleUpdateNote = ({ id, title, content }) => {
const _notes = [];
for (let i = 0; i < notes.length; i++) {
if (i === id) {
_notes.push({ id, title, content });
} else {
_notes.push(notes[i]);
}
}
setNotes(_notes);
};
return (
<div>
<CreateArea addNote={addNote} />
{notes.map((note, index) => {
return (
<Note
key={index}
id={index}
title={note.title}
content={note.content}
deleteNote={deleteNote}
handleUpdateNote={handleUpdateNote}
/>
);
})}
</div>
);
}
function App() {
return (
<div>
<Container />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
Also, you can store the notes in an object or hash map instead of an array. For example
const [notes, setNotes] = React.useState({
'unique_id': { title: 'Words', content: 'hello, bye' }
});
Then in handleUpdateNote you have
setNotes((prev) => ({ ...prev, unique_id: { title, content } }))
I need to click on the button to delete the line with the selected checkbox.
I don't know how to use the filter method. I read the documentation, but there is little information there. help me correct the code
class Table extends Component {
constructor(props) {
super(props);
this.state = {
droplets: [],
allSelected: false,
isChecked: false
}
this.toggleSelectAll = this.toggleSelectAll.bind(this);
this.toggleSelect = this.toggleSelect.bind(this);
this.handleChecked = this.handleChecked.bind(this);
**this.handleDelete = this.handleDelete.bind(this);**
}
async componentDidMount() {
const res = await fetch('http://api.npoint.io/324f4ca2cdd639760638');
const droplets = await res.json();
this.setState({ 'droplets': droplets })
}
toggleSelect(dropletToToggle) {
this.setState({isChecked: !this.state.isChecked});
this.setState((prevState) => {
const newDroplets = prevState.droplets.map((dplt) => {
if (dplt === dropletToToggle) {
return { ...dplt, checked: !dplt.checked };
}
return dplt;
});
return {
...prevState,
droplets: newDroplets,
allSelected: newDroplets.every((d) => !!d.checked)
};
});
}
toggleSelectAll() {
this.setState({isChecked: !this.state.isChecked});
this.setState((prevState) => {
const toggle = !prevState.allSelected;
const newDroplets = prevState.droplets.map((x) => ({
...x,
checked: toggle
}));
return {
...prevState,
droplets: newDroplets,
allSelected: toggle
};
});
}
handleChecked () {
this.setState({isChecked: !this.state.isChecked});
}
**handleDelete = isChecked => {
this.setState(state => {
const { droplets } = state;
const filteredDroplets = droplets.filter(item => item.id !== isChecked);
return {
droplets: filteredDroplets
};
});
};**
render() {
return (
<div className="body">
<div className="title">Таблица пользователей</div>
<table className="panel">
<Tablehead
toggleSelectAll={this.toggleSelectAll}
allSelected={this.state.allSelected}
/>
<tbody className="row">
<TableBody
droplets={this.state.droplets}
toggleSelect={this.toggleSelect}
/>
</tbody>
</table>
**<button className="button" onClick = {this.handleDelete} >Удалить выбранные</button>**
</div>
);
}
}
the second file in which the item to delete
const TableBody = ({ droplets, toggleSelect}) => {
return (
<>
{droplets.map((droplet, item) => (
<tr className={s.area} key={item.id} >
<td>
<Checkbox
handleClick={() => toggleSelect(droplet)}
isChecked={!!droplet.checked}
/>
</td>
<td>{droplet.num}</td>
<td>{droplet.first_name + " " + droplet.last_name}</td>
<td>{date_form(droplet.date_of_birth)}</td>
<td>{height_form(droplet.height)}</td>
<td>{weight_form(droplet.weight)}</td>
<td>{salary_form(droplet.salary)}</td>
<td>
<button type="submit" className={s.button}>
<Edit />
</button>
</td>
<td>
<button type="submit" className={s.button}>
<Trash />
</button>
</td>
</tr>
))}
</>
);
};
https://codesandbox.io/s/sweet-butterfly-0s4ff?file=/src/Table.jsx
I have altered your sandbox and added some functionality in order to delete the rows.
The comparison is based on full name but you can change it to whatever fits your needs.
https://codesandbox.io/s/keen-silence-i51wz
On your handleDelete function your filter condition seems wrong
Currently you have:
// Filters all droplets that have an id different to the value of isChecked.
const filteredDroplets = droplets.filter(item => item.id !== isChecked);
And it should be
// Returns all the droplets that are not checked
// Meaning that all the checked items are dropped from the array
const filteredDroplets = droplets.filter(item => !item.isChecked);
My search form is dynmically created using a ajax call for the inputs. Each input can then be used alone or in combination with other inputs to narrow down the search results. The problem I am having is that the submit method is running a new search each time an additional input is added to the form. For example: User just searches with one input. Submit method runs once. User searches with two inputs. Search runs once for the single input and then another time for the two inputs. And so on...
Here is my parent file..
class SearchPage extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
labels: [],
contracts: [],
formValues:[],
pdfs:[],
titles:[],
alertShow: false,
show: false,
};
this.onClick = this.handleContract.bind(this);
this.handleShow = this.handleShow.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleShowAlert = this.handleShowAlert.bind(this);
this.handleCloseAlert = this.handleCloseAlert.bind(this)
}
initialState = {
formValues: {},
}
state = this.initialState
componentDidMount(){
this.loadLabels();
}
componentWillUnmount(){
}
toggleHidden () {
this.setState({
isHidden: !this.state.isHidden
})
}
handleFormReset = () => {
this.setState(() => this.initialState)
this.setState({contracts:[]})
}
handleClose() {
this.setState({ show: false });
}
handleShow() {
this.setState({ show: true });
}
handleCloseAlert() {
this.setState({ alertShow: false });
}
handleShowAlert() {
this.setState({ alertShow: true });
}
loadLabels = () => {
API.getLabels()
.then(res => {
const labels = res.data;
this.setState({ labels })
})
.catch(err => console.log(err));
};
handleInputChange = (key, value) => {
const newFormValues = Object.assign({}, this.state.formValues, {[key]: value});
this.setState({ formValues: newFormValues })
};
handleContract = (id) => {
API.openRow(id)
.then(res => {
const pdfs = res.data;
this.setState({pdfs});
this.props.history.push({
state: { labels:this.state.labels,
pdfs:this.state.pdfs,
titles:this.state.titles }
})
})
.catch(err => console.log(err));
API.titles(id)
.then(res => {
const titles = res.data;
this.setState({titles});
})
this.setState({ show: true });
}
handleFormSubmit = event => {
event.preventDefault();
const formData = this.state.formValues
let query = '';
let keys = Object.keys(formData);
keys.map(k => {
if (query !== "")
query += `&`;
query += `filter=`
query += `${k}|${formData[k]}`
return this.loadContracts(query);
})
};
noResults() {
this.setState({alertShow:true})
}
loadContracts = (query) => {
API.search(query)
.then(res => {
const contracts = res.data;
if (contracts.length > 0 ){
this.setState({ contracts });
}
else {
this.noResults();
this.setState({contracts:[]});
};
})
.catch(err => console.log(err));
};
render() {
return (
<div className="container" style={{ marginTop: "80px" }}>
<div className="jumbotron">
<div className="container">
<h1>Contract Document Search</h1>
</div>
<br/>
<Container>
<SearchForm
labels={this.state.labels}
handleFormSubmit={this.handleFormSubmit}
handleInputChange={this.handleInputChange}
handleReset={this.handleReset}
handleFormReset={this.handleFormReset}
/>
<div className='modal'>
<Modal show={this.state.alertShow}
onHide={this.handleCloseAlert}
{...this.props}
size="sm"
aria-labelledby="contained-modal-title-vcenter"
centered>
<Modal.Header closeButton>
<Modal.Body>No results found</Modal.Body>
</Modal.Header>
</Modal>
</div>
</Container>
</div>
<div className="container">
<div className="jumbotron-fluid">
</div>
<SearchResults
labels={this.state.labels}
contracts={this.state.contracts}
pdfs={this.state.pdfs}
handleContract={this.onClick}
handleTitles={this.onClick}
/>
<br/>
<br/>
</div>
);
}
}
export default SearchPage;
And My search form component..
export default class SearchForm extends Component {
constructor(...args) {
super(...args);
this.state = {
};
}
render() {
return (
<form className="form-inline col-md-12" onReset={this.props.handleFormReset}>
{this.props.labels.map(label => (
<div className="card border-0 mx-auto" style={styles} key={label.Id}>
<ul className="list-inline ">
<span>
<li>
<Labels htmlFor={label.DisplayName} >{label.DisplayName}:</Labels>
</li>
<li >
<Input
key={label.Id}
onChange={(event) => {
this.props.handleInputChange(label.DataField, event.target.value)}}
value={this.props.newFormValues}
maxLength="999"
style={{height:34}}
name="value"
type="search"
className={"form-control mb-2 mr-sm-2"}
id={label.DataField}
/>
</li>
</span>
</ul>
</div>
))}
<div className=" col-sm-12">
<Button
style={{ float: "left", marginBottom: 10 }}
className="btn btn-success"
type="submit"
onClick={this.props.handleFormSubmit}
>
Search
</Button>
<Help />
<Button
style={{ float: "left", marginBottom: 10 }}
className="btn btn-secondary"
type="reset"
onClick={this.props.handleFormReset}
>
Reset
</Button>
</div>
</form>
);
}
}
The problem was that I had my return statement inside the input mapping.
handleFormSubmit = event => {
event.preventDefault();
const formData = this.state.formValues
let query = '';
let keys = Object.keys(formData);
keys.map(k => {
if (query !== "")
query += `&`;
query += `filter=`
query += `${k}|${formData[k]}`
**return this.loadContracts(query);**
})
};
Solved by moving the return statement outside the mapping.
handleFormSubmit = event => {
event.preventDefault();
const formData = this.state.formValues
let query = '';
let keys = Object.keys(formData);
keys.map(k => {
if (query !== "")
query += `&`;
query += `filter=`
query += `${k}|${formData[k]}`
})
**return this.loadContracts(query);**
};