I'm developing an application in react.Js
And I have a table that I need to update without having to refresh the page.
The code:
const App = () => {
const [item, setItem] = useState([])
const [category, setCategory] = useState([])
const categories = category.map(elem => ({
value: elem.id,
label: elem.cat,
}));
const [category_select, setCategorySelect] = React.useState(null);
function handleChangeCategory(value) {
setCategorySelect(value);
}
useEffect(() => {
getItems()
}, [])
useEffect(() => {
getCategories()
}, [])
const getItems = async () => {
const response = await axios.get(REQUEST_1)
setItem(response.data)
}
const getCategories = async () => {
const response = await axios.get(REQUEST_2)
setCategory(response.data)
}
const addItems = () => {
axios.post(`${REQUEST_1}`, {cat: category_select.value});
};
const body = () => {
return item && item.map((elem,j) => {
return (
<tr key={elem.id}>
<td><span>{elem.cat}</span></td>
</tr>
)
})
}
return (
<>
<div>
<div>
<div>
<div>
<NoSsr>
<Select
classes={classes}
styles={selectStyles}
inputId="category"
TextFieldProps={{
label: 'Category',
InputLabelProps: {
htmlFor: 'category',
shrink: true,
},
placeholder: 'Search',
}}
options={categories}
components={components}
value={category}
onChange={handleChangeCategory}
/>
</NoSsr>
<span>Select</span>
</div>
<div>
<label> </label>
<span onClick={addItems}></span>
</div>
</div>
<div>
<div>
<div>
<table>
<thead>
<tr>
<th>
<span>Category</span>
</th>
</tr>
</thead>
<tbody>
{body()}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default App;
The idea is that a category is selected and the item is added.
What I need is to be able to update the map (return item && item.map((elem,j) => {...})) after clicking on <span onClick={addItems}></span> which calls the addItems function that adds the item. Simply put I need it to be added in the table without having to update.
How can I do it, suggestions?
You need to add the dependency to the useEffect so that every time the user wants adds an item, getItems() request is called to get the new items. So, your code might look like something like this:
const App = () => {
const [item, setItem] = useState([])
const [category, setCategory] = useState([])
const [addedItem, setAddedItem] = useState('')
const categories = category.map(elem => ({
value: elem.id,
label: elem.cat,
}));
const [categorySelect, setCategorySelect] = React.useState(null);
function handleChangeCategory(value) {
setCategorySelect(value);
}
useEffect(() => {
getItems()
}, [addedItem])
useEffect(() => {
getCategories()
}, [])
const getItems = async () => {
const response = await axios.get(REQUEST_1)
setItem(response.data)
}
const getCategories = async () => {
const response = await axios.get(REQUEST_2)
setCategory(response.data)
}
const addItems = () => {
const response = await axios.post(`${REQUEST_1}`, {cat: category_select.value});
setAddedItem(response.data)
};
const body = () => {
return item && item.map((elem,j) => {
return (
<tr key={elem.id}>
<td><span>{elem.cat}</span></td>
</tr>
)
})
}
return (
<>
<div>
<div>
<div>
<div>
<NoSsr>
<Select
classes={classes}
styles={selectStyles}
inputId="category"
TextFieldProps={{
label: 'Category',
InputLabelProps: {
htmlFor: 'category',
shrink: true,
},
placeholder: 'Search',
}}
options={categories}
components={components}
value={category}
onChange={handleChangeCategory}
/>
</NoSsr>
<span>Select</span>
</div>
<div>
<label> </label>
<span onClick={addItems}></span>
</div>
</div>
<div>
<div>
<div>
<table>
<thead>
<tr>
<th>
<span>Category</span>
</th>
</tr>
</thead>
<tbody>
{body()}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default App;
Related
Please help me! Delete Icon is not functional, when I click on delete icon it delete all the contact, on refreshing, it returns all the previous contacts. I am also using localStorage.
I have added all the Component of the React App Project.
App.js
import { v4 as uuid } from "uuid";
const App = () => {
const LOCAL_STORAGE_KEY = "contacts";
const [contacts, setContacts] = useState([]);
const addContactHandler = (contact) => {
console.log(contact);
setContacts([...contacts, { id: uuid(), ...contact }]);
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
};
useEffect(() => {
const retrieveContacts = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEY)
);
if (retrieveContacts) {
setContacts(retrieveContacts);
}
}, []);
useEffect(() => {
if (contacts.length) {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}
}, [contacts]);
return (
<>
<div className="app">
<Header />
<AddContact addContactHandler={addContactHandler} />
<ContactList contacts={contacts} getContactId={removeContactHandler} />
</div>
</>
);
};
export default App;
ContactList.js
const ContactList = (props) => {
const deleteContactHandler = (id) => {
props.getContactId(id);
};
const renderContactList = props.contacts.map((contact) => {
return (
<>
<ContactCard
contact={contact}
clickHandler={deleteContactHandler}
key={contact.id}
/>
</>
);
});
return (
<>
<div className="contactList">
<h2 className="contactList__title">Contact List</h2>
<div className="contactList__container">
{renderContactList}
</div>
</div>
</>
);
};
ContactCard.js
const ContactCard = (props) => {
const { id, name, email } = props.contact;
return (
<>
<div className="contactCard">
<div className="contactCard__contact">
<img
className="contactCard__userIcon"
src={userIcon}
alt="user-icon"
/>
<div className="contactCard__userName">
<h2>{name}</h2>
<p>{email}</p>
</div>
</div>
<div className="contactCard__delIcon">
<img
src={delIcon}
alt="del-icon"
onClick={() => props.clickHandler(id)}
/>
</div>
</div>
</>
);
};
export default ContactCard;
I have researched out the references. Unable to get the Solution.
The effect to store the contacts do not save empty arrays.
Thats why you get the old array after refreshing your page.
Just remove the condition.
useEffect(() => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}, [contacts]);
But you should consider to remove this effect.
Save the contacts directly after setting the state instead.
const addContactHandler = (contact) => {
console.log(contact);
const newContactList = [...contacts, { id: uuid(), ...contact }];
setContacts(newContactList);
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newContactList));
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newContactList));
};
I have one button in front each list item when I click on the watched button, I want the text of the button to be changed to not watched(or when click on not watched it change to watched) and to be included in the watched list. At the top, I have three buttons, watched and not watched , which one I clicked on. Show me the list of the movies that I changed, their state and for third button(with text of all )it shows the whole list.I think that my problem is handleWatchedBtn function . this is picture of project maybe it is simple my explanation! thank you for your help.
import React, { useEffect, useState } from "react";
const App = () => {
const [Movies, setMovie] = useState([]);
const [Loading, setLoading] = useState(true);
const [Keyword, setKeyword] = useState("");
const [OverSeven, setOverSeven] = useState(false);
const [filterByWatch, setfilterByWatch] = useState("ALL");
useEffect(() => {
fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
setMovie(
response.map((item) => ({
id: item.id,
name: item.name,
rate: item.rate,
watched: false,
}))
);
setLoading(false);
});
}, []);
function handleWatchedBtn(id) {
setMovie(() =>
Movies.map((movie) => {
if (movie.id === id) {
return { movie, watched: !movie.watched };
}
return movie;
})
);
}
function handleWatchedChange(filter) {
setfilterByWatch({ filterByWatch: filter });
}
function handleKeywordChange(e) {
setKeyword(e.target.value);
}
function handleOverSevenChange(e) {
setOverSeven(e.target.checked);
}
function filterItems() {
return Movies.filter((item) =>
item.name.toLowerCase().includes(Keyword.toLowerCase())
)
.filter((item) => (OverSeven ? item.rate > 7 : true))
.filter((item) =>
filterByWatch === "ALL"
? true
: item.watched === (filterByWatch === "WATCHED")
);
}
if (Loading) {
return "Please wait...";
}
return (
<div>
<div>
<div>
Keyword
<input type="text" value={Keyword} onChange={handleKeywordChange} />
</div>
<div>
<button onClick={() => handleWatchedChange("ALL")}>all</button>
<button onClick={() => handleWatchedChange("WATCHED")}>watch</button>
<button onClick={() => handleWatchedChange("NOT_WATCHED")}>
not watch
</button>
</div>
<div>
Only over 7.0
<input
type="checkbox"
checked={OverSeven}
onChange={handleOverSevenChange}
/>
</div>
<div>
<ul>
{filterItems().map((movie) => (
<li data-id={movie.id}>
{`${movie.name} ${movie.rate}`}{" "}
<button onClick={() => handleWatchedBtn(movie.id)}>
{movie.watched ? "Watched" : " Not watched"}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
You could introduce a new array state which stores all filtered movies and is updated every time the movies or the filter are updated.
You can then pass its reference to the map function that generates the list.
Also, notice that I've added the spread operator (...) in the handleWatchedBtn function and adjusted the handleWatchedChange method to update the state to a string and not an object.
Try to change your code like this:
import React, { useEffect, useState } from "react";
const App = () => {
const [Movies, setMovie] = useState([]);
const [filteredMovies, setFilteredMovies] = useState([]);
const [Loading, setLoading] = useState(true);
const [Keyword, setKeyword] = useState("");
const [OverSeven, setOverSeven] = useState(false);
const [filterByWatch, setfilterByWatch] = useState("ALL");
useEffect(() => {
fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
const newMovies = response.map((item) => ({
id: item.id,
name: item.name,
rate: item.rate,
watched: false,
}));
setMovie(newMovies);
setLoading(false);
});
}, []);
useEffect(() => {
// Update filtered movies when the data or the filter changes
const newFilteredMovies = Movies.filter((item) =>
item.name.toLowerCase().includes(Keyword.toLowerCase())
)
.filter((item) => (OverSeven ? item.rate > 7 : true))
.filter((item) =>
filterByWatch === "ALL"
? true
: item.watched === (filterByWatch === "WATCHED")
);
setFilteredMovies(newFilteredMovies);
}, [Movies, filterByWatch]);
function handleWatchedBtn(id) {
setMovie(() =>
Movies.map((movie) => {
if (movie.id === id) {
// Add the spread operator here
return { ...movie, watched: !movie.watched };
}
return movie;
})
);
}
function handleWatchedChange(filter) {
// Change this line
setfilterByWatch(filter);
}
function handleKeywordChange(e) {
setKeyword(e.target.value);
}
function handleOverSevenChange(e) {
setOverSeven(e.target.checked);
}
if (Loading) {
return "Please wait...";
}
return (
<div>
<div>
<div>
Keyword
<input type="text" value={Keyword} onChange={handleKeywordChange} />
</div>
<div>
<button onClick={() => handleWatchedChange("ALL")}>all</button>
<button onClick={() => handleWatchedChange("WATCHED")}>watch</button>
<button onClick={() => handleWatchedChange("NOT_WATCHED")}>
not watch
</button>
</div>
<div>
Only over 7.0
<input
type="checkbox"
checked={OverSeven}
onChange={handleOverSevenChange}
/>
</div>
<div>
<ul>
{filteredMovies.map((movie) => (
<li data-id={movie.id}>
{`${movie.name} ${movie.rate}`}{" "}
<button onClick={() => handleWatchedBtn(movie.id)}>
{movie.watched ? "Watched" : " Not watched"}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
Below is my code, as of now its working perfectly but I am not able to add a search bar in
it. I am new to the React.js I tried many solutions but didn't get the solution and also I need a functionality for update and delete buttons. thanks in advance
import React from "react";
import { db } from "../firebase";
import "../App.css";
function TillDateData() {
const [contacts, setContacts] = React.useState([]);
React.useEffect(() => {
const fetchData = async () => {
const data = await db.collection("contacts").orderBy("createdDate").get();
setContacts(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
fetchData();
}, []);
return (
<div className="myclass">
<table className="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">City</th>
<th scope="col">Contact No.</th>
<th scope="col">Date</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{Object.keys(contacts).map((id) => {
return (
<tr key={id}>
<td>{contacts[id].name}</td>
<td>{contacts[id].email}</td>
<td>{contacts[id].city}</td>
<td>{contacts[id].contact}</td>
<td>{contacts[id].createdDate}</td>
<td>
<button className="btn btn-primary btn-sm">Edit</button>
</td>
<td>
<button className="btn btn-danger btn-sm">Delete</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
export default TillDateData;
import React, { useState } from "react";
import { db, fire } from "../firebase";
import "../App.css";
function TillDateData() {
const [search, setSearch] = useState("");
const [users, setUsers] = React.useState([]);
const [contactsData, setContactsData] = useState(users); //iterate this in table
const deleteContact = (id) => {
db.collection('users').doc(id).delete()
};
const changeSearch = (val) => {
setSearch(val);
if (val != "") {
setContactsData(
users.filter((contact) => {
contact.name.toLowerCase().includes(val.toLowerCase()) ||
contact.email.toLowerCase().includes(val.toLowerCase()) ||
contact.city.toLowerCase().includes(val.toLowerCase()) ||
contact.number.toLowerCase().includes(val.toLowerCase());
})
);
} else {
setContactsData(users);
}
};
React.useEffect(() => {
const fetchData = async () => {
const data = await db.collection("users").orderBy("createdDate").get();
setUsers(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
fetchData();
}, []);
return (
<>
<input type="text" onChange={(e) => changeSearch(e.target.value)} />
<div className="myclass">
<table className="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">City</th>
<th scope="col">Contact No.</th>
<th scope="col">Date</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{Object.keys(contactsData).map((id) => {
return (
<tr key={id}>
<td>{users[id].name}</td>
<td>{users[id].email}</td>
<td>{users[id].city}</td>
<td>{users[id].number}</td>
<td>{users[id].createdDate}</td>
<td>
<button className="btn btn-primary btn-sm">Edit</button>
</td>
<td>
<button
className="btn btn-danger btn-sm"
onClick={()=> deleteContact(contact[id].id)}
>
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</>
);
}
export default TillDateData;
just changed some variables remaining same
After a lot of struggle I found a way how to add a search bar functionality in firestore retrieved data. here is my code
import React, { useState, useEffect } from "react";
import { db } from "../firebase";
import "../App.css";
function RetrieveData() {
const [contacts, setContacts] = useState([]);
const [search, setSearch] = useState("");
const [filteredContacts, setFilteredContacts] = useState([]);
useEffect(() => {
const fetchData = async () => {
const data = await db.collection("contacts").orderBy("createdDate").get();
setContacts(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
fetchData();
}, []);
useEffect(() => {
setFilteredContacts(
contacts.filter(
(user) =>
user.name.toLowerCase().includes(search.toLowerCase()) ||
user.city.toLowerCase().includes(search.toLowerCase())
)
);
}, [search, contacts]);
return (
<>
<div className="App">
<h1>Contact Details</h1>
<input
type="text"
placeholder="Search"
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<div>
{filteredContacts.map((contact) => [
<ol>
<b>Consumer Details :</b> {<br />}
{contact.name},{<br />}
{contact.email},{<br />}
{contact.city},{<br />}
{contact.contact},{<br />}
</ol>,
])}
</div>
</>
);
}
export default RetrieveData;
For delete an update try the following code
const deleteContact = (id) => {
//Delete contact with the passed id
}//same for update or edit contacts
<tr>
....
<td><button onClick={()=> deleteContact(contact[id].id)}></td>//same for
update
</tr>
Search Bar
For search bar Do following steps
1- Make search state
const [search, setSearch] = useState('')
const [contacts, setContacts] = useState([])//used just to store contacts from api
const [contactsData, setContactsData] = useState(contacts)//iterate this in table
2- Write an searchChange handler
const changeSearch = (val) => {
setSearch(val)
if(val!=''){
setContactsData(contacts.filter(constact => {
contact.name.includes(val) ||
contact.fname.includes(val)//same other fields added by following OR
condition
}))
}
else{
setContactsData(contacts)
}
}
3- Make a search text field
<input type='text' onChange={(e)=> changeSearch(e.target.value)}
Note: use Objects.keys(contactsData).map after updation
Update and Delete function
1- for update
const UpdateContent = (id) => {
//call an api that delete the content from DB(firebase) for the provided id in parameters or in body.
}
component:
<tr>
Delete functionality also describe in the top section of answer
.....
<button onClick={() => UpdateContent(content[i].id)}/>
I am quite new to react and react-table, previously I had run the following code successfully on client-side filtering and now I am trying to update it to server-side filtering. My code looks as:
const ServerSideTable = ({ columns, data, tableMeta, fetchData }) => {
const defaultColumn = React.useMemo(() => {
return {
Filter: ColumnFilter,
};
}, []);
const {
getTableProps,
getTableBodyProps,
headerGroups,
// rows,
prepareRow,
state,
setGlobalFilter,
page,
} = useTable(
{
columns,
data,
defaultColumn,
manualPagination: true,
pageCount: tableMeta.totalPage,
},
useFilters,
useGlobalFilter,
useSortBy,
usePagination
);
<table
{...getTableProps()}
id="basicTable"
>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
{...column.getHeaderProps()}
className="align-top"
>
<div
{...column.getSortByToggleProps()}
>
{column.render("Header")}
</div>
<div>
{column.canFilter
? column.render(
"Filter"
)
: ""}
</div>
</th>
))}
</tr>
))}
</thead>
</table>
}
and ColumnFilter looks as
const ColumnFilter = ({ column }) => {
const { filterValue, setFilter } = column;
const [value, setValue] = useState(filterValue);
const onChange = useAsyncDebounce((value) => {
setFilter(value || undefined);
}, 1000);
return (
<input
type="search"
className="form-control form-control-sm d-inline-block"
placeholder=""
aria-controls="datatable"
value={value || ""}
onChange={(e) => {
setValue(e.target.value);
onChange(e.target.value);
}}
/>
);
};
What I want here, is the callback handleSearchquuery as const ColumnFilter = ({ column, handleSearchQuery }) => {
so that instead of calling setFilter on onChange I could call the callback.
At the moment, this filter is being rendered as:
{column.canFilter
? column.render(
"Filter"
)
: ""}
Looks like this is the related docs, but couldn't get the idea.
https://react-table.tanstack.com/docs/api/useFilters#column-options
I'm developing an application in React.JS
I need to put a condition before inserting.
The code:
const App = () => {
const [item, setItem] = useState([])
const [category, setCategory] = useState([])
const categories = category.map(elem => ({
value: elem.id,
label: elem.cat,
}));
const [category_select, setCategorySelect] = React.useState(null);
function handleChangeCategory(value) {
setCategorySelect(value);
}
useEffect(() => {
getItems()
}, [])
useEffect(() => {
getCategories()
}, [])
const getItems = async () => {
const response = await axios.get(REQUEST_1)
setItem(response.data)
}
const getCategories = async () => {
const response = await axios.get(REQUEST_2)
setCategory(response.data)
}
const addItems = () => {
axios.post(`${REQUEST_1}`, {cat: category_select.value});
};
const body = () => {
return item && item.map((elem,j) => {
return (
<tr key={elem.id}>
<td><span>{elem.cat}</span></td>
</tr>
)
})
}
return (
<>
<div>
<div>
<div>
<div>
<NoSsr>
<Select
classes={classes}
styles={selectStyles}
inputId="category"
TextFieldProps={{
label: 'Category',
InputLabelProps: {
htmlFor: 'category',
shrink: true,
},
placeholder: 'Category...',
}}
options={categories}
components={components}
value={category}
onChange={handleChangeCategory}
/>
</NoSsr>
<span>Select</span>
</div>
<div>
<label> </label>
<span onClick={addItems}></span>
</div>
</div>
<div>
<div>
<div>
<table>
<thead>
<tr>
<th>
<span>Category</span>
</th>
</tr>
</thead>
<tbody>
{body()}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default App;
The idea is to check if the item to be inserted already exists, and if it is already inserted, show a message through a popup.
How do I add this condition in the const addItems = () => {...} and the popup? Do I have to put that condition here?
How can I do it, suggestions?
You should check if the selected category already exists in addItems method.
You can use React packages like reactjs-popup to display a popup.
const [isShow, setIsShow] = useState(false)
const addItems = () => {
// check if selected category already exists
if (!category.some(elem => elem.cat === category_select.value)) {
axios.post(`${REQUEST_1}`, {cat: category_select.value});
}
else {
// update popup modal open state and display popup
setIsShow(true);
}
};
...
return (
...
{ isShow && <h1>Error</h1> }
...)