I am trying to delete items from my MERN stack app using fetch and I have successfully deleted the item in the database. However, I cannot figure out how to delete the item from state when I click the delete button. I am using the ID of the item (groupID) to delete from the database, and I'm trying to use that to delete from state as well. Right now when clicking the button the item deletes but does not disappear until i reload the page. Here is my code:
const [value, setValue] = useState("");
const [groupsState, setGroups] = useState({});
const [loading, setLoading] = useState(true);
const { isDarkMode } = useContext(ThemeContext);
const { token } = useContext(LoggedInContext);
const handleClick = (groupId) => {
function removeGroup(groupId) {
setGroups(groupsState.filter(el => el !== groupId));
}
fetch("http://localhost:8181/groups/" + groupId, {
headers: {
"Content-Type": "application/json",
Token: token
},
method: "DELETE"
})
.then((res) => {
return res.json();
})
.then((data) => {
console.log(data.data)
removeGroup(groupId);
})
};
If you're set on removing it in JavaScript, you'll just need to specify the id key in your filter
setGroups(groupsState.filter(el => el.id !== groupId));
That's assuming it's called id, otherwise just change it to what your id key is.
Related
I'm using antd with react to upload an image to each of my facilities.
The uploading is working, but I can't seem to get the preview from the previous image to show up where it should, by pulling the existing image from the database.
What happens is that it will only show the new image, after it has been uploaded via drag and drop but not the previous one stored in the database. I'm pulling the url of the previous image with the const "testing" and I can log it and it will show inside the console but it will not show when I use it in the setFileList const. What am I missing here?
export function FacilityUpdate() {
const navigate = useNavigate()
const { id } = useParams()
const [facility, setFacility] = useState(null)
const accessToken = useSelector((state) => state.tokens.value.accessToken);
const [loadingFacility, setLoadingFacility] = useState(false)
const [loading, setLoading] = useState(false)
const dispatch = useDispatch();
useEffect(() => {
if (facility && !facility.is_owner) {
navigate('/facilities')
}
return () => null
})
useEffect(() => {
setLoadingFacility(true)
function fetchFacility() {
axios.get(API.facilities.retrieve(id), {
headers: {
"Authorization": `Bearer ${accessToken}`
},
withCredentials: true,
})
.then(res => {
setFacility(res.data)
})
.finally(() => {
setLoadingFacility(false)
})
}
fetchFacility()
return () => null
}, [id, accessToken])
const testing = facility && (facility.mainimage)
console.log(testing)
const [fileList, setFileList] = useState([
{
uid: '-1',
name: testing,
status: 'done',
//This is where the preview image gets loaded from the database:
url: testing,
},
]);
const onUploadDraggerChange = ({ fileList: newFileList, file: resp }) => {
setFileList(newFileList);
if (!resp || !resp.response || !resp.response.mainimage) {
return;
}
message.success("Updated facility picture")
};
const uploadDraggerrops = {
name: 'mainimage',
action: API.facilities.update(id),
method: 'PATCH',
listType: "picture-card",
maxCount: 1,
onChange: onUploadDraggerChange,
fileList: fileList,
headers: {
"Authorization": `Bearer ${accessToken}`
},
withCredentials: true,
};
The problem with your code might come from this line on onUploadDraggerChange:
setFileList(newFileList);
Before updating the state of fileList, fileList is an array with an element that contains that previous picture. When you call onUploadDraggerChange you are erasing the stored content and replacing it with the new one. Maybe you want to push it to add to the array?
setFileList([...fileList, newFileList]);
This way, the first element is the one fetched and the consequent ones from the draggerUpload.
Any case, it looks like the structure of your state should look like
const [facility, setFacility] = useState(null)
const [fileList, setFileList] = useState([]);
useEffect(() => {
if (facility && !facility.is_owner) {
navigate('/facilities')
}
return () => null
})
useEffect(() => {
setLoadingFacility(true)
function fetchFacility() {
axios.get(API.facilities.retrieve(id), {
headers: {"Authorization": `Bearer ${accessToken}`},
withCredentials: true,
})
.then(res => {
setFacility(res.data)
res && (res.mainimage) {
setFileList([...filelist, {
uid: 'some-random-id',
name: testing,
status: 'done',Ï
url: res.mainimage
}])
})
.finally(() => {
setLoadingFacility(false)
})
}
fetchFacility()
return () => null
}, [id, accessToken])
I am building a website using nextjs and axios. Users can apply to become a member and then be approved by admins. In the admin dashboard I initially load the users and the unapproved users and display them in a list.
When an admin clicks on a button the unapproved user should be approved. The functionality works. The only aspect I can't figure out is how to update the state.
Here is my code:
const AdminIndex = () => {
const [users, setUsers] = useState([])
const [unapprovedUsers, setUnapprovedUsers] = useState([])
useEffect(() => {
loadUnapprovedUsers()
loadUsers()
}, [])
const loadUnapprovedUsers = async () => {
const { data } = await axios.get('/api/admin/unapprovedUsers')
setUnapprovedUsers(data)
}
const loadUsers = async () => {
const { data } = await axios.get('/api/admin/users')
setUsers(data)
}
const approveUnapprovedUser = async (email) => {
try {
const { data } = await axios.put(
`/api/admin/approveUnapprovedUser/${email}`
)
setUnapprovedUsers([]) // only remove the approved user
setUsers(...data) // include the approved user into the array
} catch (err) {
console.log(err)
}
}
}
I am trying to remove the approved user from the unapprovedUsers array and try to add the user to the users array, hence updating the UI. The response returned by axios is an object, which doesn't make things easier.
I would be very thankful for any kind of help!
Just try to filter the unapprovedUsers with the users that don't have that email, also add the approved user to users state
const AdminIndex = () => {
const [users, setUsers] = useState([])
const [unapprovedUsers, setUnapprovedUsers] = useState([])
useEffect(() => {
loadUnapprovedUsers()
loadUsers()
}, [])
const loadUnapprovedUsers = async () => {
const { data } = await axios.get('/api/admin/unapprovedUsers')
setUnapprovedUsers(data)
}
const loadUsers = async () => {
const { data } = await axios.get('/api/admin/users')
setUsers(data)
}
const approveUnapprovedUser = async (email) => {
try {
const { data } = await axios.put(
`/api/admin/approveUnapprovedUser/${email}`
)
setUnapprovedUsers(prev => prev.filter(user => user.email !== email)) // only remove the approved user
setUsers(prev => [...prev, data]) // include the approved user into the array
} catch (err) {
console.log(err)
}
}
}
First part of my code:
const Inventory = () => {
const [products, setProducts] = useState([]);
const [pageCount,setPageCount] = useState(0);
//console.log(pageCount);
const [page,setPage] = useState(0);
const navigate = useNavigate();
useEffect(() => {
fetch(`http://localhost:3000/products?page=${page}`)
.then((res) => res.json())
.then((data) => setProducts(data));
}, [products,page]);
useEffect(()=>{
axios.get('http://localhost:3000/product-count')
.then((res) => {
const productCount = res?.data?.count;
//console.log("count product",count);
const pages = Math.ceil(productCount/6);
setPageCount(pages);
})
},[page])
Return section, this code will return pagination:
<div className="pagination">
{
[...Array(pageCount).keys()]
.map(number => <button key={number} className={page===number? 'selected': 'pagination'}
onClick={()=> setPage(number)}
>
{number+1}
</button>)
}
</div>
And this is the server-side code:
app.get("/products",async(req,res)=>{
const page = parseInt(req.query.page);
//console.log(page);
const q = {};
const cursor = productCollection.find(q);
let result;
if(page){
result = await cursor.skip(page * 6).limit(6).toArray();
}
else{
result = await cursor.toArray();
}
res.send(result);
})
// get product count
app.get("/product-count",async(req,res)=>{
const query = {};
const count = await productCollection.countDocuments(query);
res.json({count});
})
I want to load 6 data on the first load. But when data loads it displays all data. When I click the pagination button it works fine except the 1st button.
During the first load your front-end is sending page=0 to your backend.
In your server-side code you've the following statement: if (page)
But page will always be false when page=0, because 0 is a falsy value in javascript. So your statement always return the else block.
You need to change the statement to:
if (page !== undefined)
I have a problem with one of my components. The problem I think I have is that my component executes before my user context stores the currentUser. My code only works when doing a hot reload.
The watchlist component gets all the values from the watchlist array where the document matches the currentUser.uid.
UserContext.js:
const [currentUser, setCurrentUser] = useState(null)
const [watchlist, setWatchlist] = useState(null)
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
})
return unsubscribe
}, [])
const getWatchlist = async () => {
const userRef = await getDoc(doc(db, 'users', currentUser.uid))
setWatchlist(userRef.data().watchlist)
console.log(userRef.data().watchlist)
}
These values are the ids of objects I then GET from an API, these are then pushed to the watchlistData array.
CryptoContext.js
export const getWatchlistData = async (list) => {
const watchlistData = []
for (const item of list) {
const result = await axios.get(
`${coingecko}/coins/${item}`
)
watchlistData.push(result.data)
}
return watchlistData
}
And this is how my Watchlist component code currently looks.
WatchlistItems.jsx
const { watchlist, getWatchlist, currentUser } = useContext(UserContext)
const { dispatch } = useContext(CryptoContext)
useEffect(() => {
if (currentUser) {
dispatch({type: 'SET_LOADING'})
const getWatchlistDataFromAPI = async () => {
await getWatchlist()
const watchlistData = await getWatchlistData(watchlist)
dispatch({type: 'GET_WATCHLIST', payload: watchlistData})
console.log(watchlistData)
}
getWatchlistDataFromAPI()
}
}, [currentUser])
If I refresh the page I get "Uncaught (in promise) TypeError: the list is not iterable", but if I do a hot reload, watchlist, and watchlistData both console.log with the correct data.
This is my first post and so please let me know if I've left anything out.
Thank you in advance for any help :)
So I'm trying out basic todo app with edit and delete feature. I'm having problems with my edit feature. I have two main components in my app namely InputTodo for adding todo items and ListTodo which contains two additional subcomponents (TodoItem for each todo and EditTodo which shows an editor for a selected todo). Whenever the Edit Button inside a certain TodoItem is clicked, the EditTodo component is showed. When the Confirm button in EditTodo component is clicked, a PUT request will be sent to update the database (PostgreSQL in this case) through Node. After successfully sending this send request, I would like to re-render the list of TodoItem components. I'm doing this by fetching the updated list of values from the database through a different GET request then calling setState given the response from the GET request. However, the GET request's response doesn't reflect the PUT request done earlier. Thus, the app still renders the un-updated list of todos from the database.
Here are some code snippets
const ListTodo = (props) => {
const [todos, setTodos] = useState([]);
const [editorOpen, setEditorOpen] = useState(false);
const [selectedId, setSelectedId] = useState();
const getTodos = async () => {
console.log('getTodos() called');
try {
const response = await fetch("http://localhost:5000/todos");
const jsonData = await response.json();
setTodos(jsonData);
console.log(todos);
} catch (err) {
console.error(err.message);
}
console.log('getTodos() finished');
};
const editTodo = async description_string => {
console.log('editTodo() called');
try {
const body = { description: description_string };
const response = await fetch(
`http://localhost:5000/todos/${selectedId}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
}
);
console.log(response);
await getTodos();
props.handleListModified();
} catch (err) {
console.error(err.message);
}
console.log('editTodo() finised');
}
const handleItemButtonClick = (button, row_key) => {
if (button === 'delete') {
deleteTodo(row_key);
setEditorOpen(false);
} else if (button === 'edit') {
setEditorOpen(true);
setSelectedId(row_key);
console.log(todos.filter(todo => { return todo.todo_id === row_key})[0].description);
}
};
const handleEditorButtonClick = async (button, description_string) => {
if (button === 'cancel') {
setSelectedId(null);
} else if (button === 'confirm') {
await editTodo(description_string);
}
setEditorOpen(false);
};
useEffect(() => {
console.log('ListTodo useEffect() trigerred');
getTodos();
}, [props.listModified]);
return(
<Fragment>
<table>
<tbody>
{todos.map( todo => (
<TodoItem
key={todo.todo_id}
todo_id={todo.todo_id}
description={todo.description}
handleClick={handleItemButtonClick} />
))}
</tbody>
</table>
{ editorOpen &&
<EditTodo
handleEditorButtonClick={handleEditorButtonClick}
description={todos.filter(todo => { return todo.todo_id === selectedId})[0].description}
selectedId={selectedId} /> }
</Fragment>
);
};
I guess that the problem is - In editTodo function, you are calling getTodos() function. But, you are not updating the state with the response you get. See if this helps.
const response = await fetch(
`http://localhost:5000/todos/${selectedId}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
}
);
console.log(response);
setTodo(await getTodos()); // Update the state with the values from fetch