Can't resolve 'react-transition-group/Transition' in 'C:\...; - node.js

I have an app which displays blogposts.
The app works perfectly without styles.
However, when I import a component from EITHER React Bootstrap OR material-ui. I get the following error message:
Can't resolve 'react-transition-group/Transition' in 'C:\Users\xxxxx\Documents\Web Development\bloglist-frontend\node_modules\react-bootstrap\esm'
How do you work around this message?
The App:
import { useState, useEffect } from "react";
import Blog from "./components/Blog";
import blogService from "./services/blogs";
import loginService from "./services/login";
import Notification from "./components/Notification";
import ErrorNotification from "./components/ErrorNotification";
import BlogForm from "./components/BlogForm";
import Togglable from "./components/Togglable";
import { useDispatch, useSelector } from 'react-redux'
import { setBlogs } from './reducers/blogReducer'
import User from './components/User'
import SingleBlog from './components/SingleBlog'
import styled from 'styled-components'
import LoginForm from './components/LoginForm'
import Blogs from './components/Blogs'
import Logout from './components/Logout'
import { Table } from 'react-bootstrap'
import {
BrowserRouter as Router,
Routes, Route, Link
} from "react-router-dom"
const Page = styled.div`
padding: 1em;
background: papayawhip;
`
const Navigation = styled.div`
background: BurlyWood;
padding: 1em;
`
const Footer = styled.div`
background: Chocolate;
padding: 1em;
margin-top: 1em;
`
const App = () => {
/* const [blogs, setBlogs] = useState([]); */
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [user, setUser] = useState(null);
const [errorMessage, setErrorMessage] = useState(null);
const [newAuthor, setNewAuthor] = useState("");
const [newTitle, setNewTitle] = useState("");
const [newUrl, setNewUrl] = useState("");
const [failureMessage, setFailureMessage] = useState(null);
const dispatch = useDispatch()
useEffect(() => {
blogService
.getAll().then(blogs => dispatch(setBlogs(blogs)))
}, [dispatch])
const blogs = useSelector(state => state)
/*
useEffect(() => {
blogService.getAll().then((blogs) => setBlogs(blogs));
}, []); */
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem("loggedBlogappUser");
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON);
setUser(user);
blogService.setToken(user.token);
}
}, []);
const handleAuthorChange = (event) => {
setNewAuthor(event.target.value);
};
const handleTitleChange = (event) => {
setNewTitle(event.target.value);
};
const handleUrlChange = (event) => {
setNewUrl(event.target.value);
};
const handleLogin = async (event) => {
event.preventDefault();
try {
const user = await loginService.login({
username,
password,
});
window.localStorage.setItem("loggedBlogappUser", JSON.stringify(user));
blogService.setToken(user.token);
setUser(user);
setUsername("");
setPassword("");
setErrorMessage(`${user.name} is now logged in!`);
setTimeout(() => {
setErrorMessage(null);
}, 5000);
} catch (exception) {
setFailureMessage("Wrong username or password");
setTimeout(() => {
setFailureMessage(null);
}, 5000);
setErrorMessage("Wrong credentials");
setTimeout(() => {
setErrorMessage(null);
}, 5000);
}
};
const handleLogout = () => {
window.localStorage.removeItem("loggedBlogappUser");
};
const padding = {
padding: 5
}
/* const createBlog = async () => {
try {
const blogObject = {
author: newAuthor,
title: newTitle,
url: newUrl,
likes: 0,
};
await blogService.create(blogObject);
setBlogs(blogs.concat(blogObject));
setNewTitle("");
setNewAuthor("");
setNewUrl("");
setErrorMessage(`${blogObject.title} has been added!`);
setTimeout(() => {
setErrorMessage(null);
}, 5000);
} catch (exception) {
setErrorMessage(`blog couldn't be added`);
}
};
*/
/* const handleLike = (id) => {
const blog = blogs.find((b) => b.id === id);
const changedBlog = { ...blog, likes: blog.likes + 1 };
blogService
.update(id, changedBlog)
.then((returnedBlog) => {
setBlogs(blogs.map((blog) => (blog.id !== id ? blog : returnedBlog)));
})
.catch((error) => {
alert(`the blog '${blog.content} was already deleted from the server`);
setBlogs(blogs.filter((b) => b.id !== id));
});
}; */
/* const handleDelete = (id) => {
const blog = blogs.find((b) => b.id === id);
if (window.confirm(`are you sure you want to remove ${blog.title}`)) {
blogService
.remove(id)
.then(setBlogs(blogs.filter((b) => b.id !== id)))
.catch((error) => {
alert(`${error}`);
});
} else {
alert("deletion cancelled");
}
};
*/
/* const arrayforSort = [...blogs] */
if (!blogs) {
return null
}
if (blogs.length > 1)
{
return (
<Router>
<Page>
<Navigation>
<div>
<Link style={padding} to="/">home</Link>
<Link style={padding} to="/blogform">blogform</Link>
<Link style={padding} to="/users">Users</Link>
<Link style={padding} to="/login">Login</Link>
</div>
</Navigation>
<Routes>
<Route path="/blogform" element={<BlogForm newAuthor={newAuthor}
newTitle={newTitle}
newUrl={newUrl}
handleAuthorChange={handleAuthorChange}
handleUrlChange={handleUrlChange}
handleTitleChange={handleTitleChange}
setErrorMessage={setErrorMessage}
blogs={blogs}/>} />
<Route path="/login" element={user === null ? (
<LoginForm handleLogin={handleLogin} setUsername={setUsername} setPassword={setPassword} username={username} password={password} />
) : (
<div>
<p>{user.name} logged-in</p>
<Logout handleLogout={handleLogout} />
</div>
)} />
<Route path="/" element={<Blogs blogs={blogs} />} />
<Route path="/users" element={<User/>} />
</Routes>
<div>
<Notification message={errorMessage} />
<ErrorNotification message={failureMessage} />
<Togglable buttonLabel="new blog">
<BlogForm
newAuthor={newAuthor}
newTitle={newTitle}
newUrl={newUrl}
handleAuthorChange={handleAuthorChange}
handleUrlChange={handleUrlChange}
handleTitleChange={handleTitleChange}
setErrorMessage={setErrorMessage}
blogs={blogs}
/>
</Togglable>
<div>
</div>
</div>
</Page>
</Router>
);
} else {
return (
<SingleBlog blogs={blogs} setBlogs={setBlogs}/>
)
}
};
export default App;

Related

[Unhandled promise rejection: FirebaseError: Function addDoc() called with invalid data. Unsupported field value: a custom Class object

I want to post data in firebase firestore database but I don't understand why that does'nt work
I Can post prewrite title in database but not with custom title from an input
traceback :
[Unhandled promise rejection: FirebaseError: Function addDoc() called with invalid data. Unsupported field value: a custom Class object (found in field date in document events/T58NdobxrvVchtaQjYx0)]
My addEvent function to add eventDate and a title to an firebase events collection
firebase.js
export async function addEvent(date, title) {
const eventsRef = collection(db, EVENTS_COLLECTION);
const newEventRef = await addDoc(eventsRef, {
date,
title,
})
.then(() => {
// Data saved successfully!
console.log("data submitted");
})
.catch((error) => {
// The write failed...
console.log(error);
});
const newEvent = await getDoc(newEventRef);
return newEvent;
}
My calendar to add data on firebase.
I have a button that open picker date time modal and next modal with an input to add data with submit and cancel button.
CalendarScreen.js
import React, { useState } from "react";
import { View, Button, StyleSheet, Modal, TextInput } from "react-native";
import { addEvent } from "../../firebase/firebase";
import CalendarPicker from "react-native-calendar-picker";
import DateTimePickerModal from "react-native-modal-datetime-picker";
export default function CalendarScreen({ selectedDate, onDateChange }) {
const [isDatePickerVisible, setIsDatePickerVisible] = useState(false);
const [isTitleModalVisible, setIsTitleModalVisible] = useState(false);
const [eventTitle, setEventTitle] = useState("");
const showDatePicker = () => {
setIsDatePickerVisible(true);
};
const hideDatePicker = () => {
setIsDatePickerVisible(false);
};
const showTitleModal = () => {
setIsTitleModalVisible(true);
};
const hideTitleModal = () => {
setIsTitleModalVisible(false);
};
const handleAddEvent = (eventDate, eventTitle) => {
hideDatePicker(eventDate, eventTitle);
showTitleModal();
};
const handleSubmitTitle = (eventDate, eventTitle) => {
addEvent(eventDate, eventTitle);
hideTitleModal();
setEventTitle("");
};
return (
<View>
<CalendarPicker
style={styles.calendar}
selectedDate={selectedDate}
onDateChange={onDateChange}
/>
<Button title="Add Event" onPress={showDatePicker} />
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="datetime"
onConfirm={handleAddEvent}
onCancel={hideDatePicker}
/>
<Modal style={styles.modal} visible={isTitleModalVisible}>
<View>
<TextInput
placeholder="Event Title"
value={eventTitle}
onChangeText={setEventTitle}
style={styles.input}
/>
<Button title="Submit" onPress={handleSubmitTitle} />
<Button title="Cancel" onPress={hideTitleModal} />
</View>
</Modal>
</View>
);
}
EDIT When string is typed
export async function getEvents() {
const eventsRef = collection(db, EVENTS_COLLECTION);
const docSnap = await getDocs(eventsRef);
const events = [];
docSnap.forEach((doc) => {
events.push({ id: doc.id, ...doc.data() });
});
return events;
}
CalendarScreen.js
export default function CalendarScreen({ selectedDate, onDateChange }) {
const [isDatePickerVisible, setIsDatePickerVisible] = useState(false);
const showDatePicker = () => {
setIsDatePickerVisible(true);
};
const hideDatePicker = () => {
setIsDatePickerVisible(false);
};
const handleAddEvent = (eventDate) => {
addEvent(eventDate);
hideDatePicker();
};
return (
<View>
<CalendarPicker selectedDate={selectedDate} onDateChange={onDateChange} />
<Button title="Add Event" onPress={showDatePicker} />
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="datetime"
onConfirm={handleAddEvent}
onCancel={hideDatePicker}
/>
</View>
);
temporary SOLUTION : See you database even with the error
CalendarScreen.js
import React, { useState } from "react";
import { View, Button, StyleSheet, Modal, TextInput } from "react-native";
import { addEvent } from "../../firebase/firebase";
import CalendarPicker from "react-native-calendar-picker";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import moment from "moment-timezone";
moment.tz.setDefault("Europe/Paris");
export default function CalendarScreen({ selectedDate, onDateChange }) {
const [isDatePickerVisible, setIsDatePickerVisible] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
const [eventTitle, setEventTitle] = useState("");
const showDatePicker = () => {
setIsDatePickerVisible(true);
};
const hideDatePicker = () => {
setIsDatePickerVisible(false);
};
const showModal = () => {
setIsModalVisible(true);
};
const hideModal = () => {
setIsModalVisible(false);
};
const handleAddEvent = (eventDate) => {
addEvent(eventDate, eventTitle);
showModal();
};
const handleSubmit = (eventDate) => {
addEvent(eventDate, eventTitle);
hideModal();
hideDatePicker();
};
return (
<View>
<CalendarPicker selectedDate={selectedDate} onDateChange={onDateChange} />
<Button title="Add Event" onPress={showDatePicker} />
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="datetime"
onConfirm={handleAddEvent}
onCancel={hideDatePicker}
/>
<Modal animationType="slide" transparent={false} visible={isModalVisible}>
<View>
<TextInput
placeholder="Event Title"
value={eventTitle}
onChangeText={setEventTitle}
/>
<Button
title="Submit"
onPress={(eventDate) => handleSubmit(eventDate)}
/>
<Button title="Cancel" onPress={hideModal} />
</View>
</Modal>
</View>
);
export async function addEvent(date, title) {
const eventsRef = collection(db, EVENTS_COLLECTION);
const newEventRef = await addDoc(eventsRef, {
date: date,
title: title,
})
.then(() => {
// Data saved successfully!
console.log("data submitted");
})
.catch((error) => {
// The write failed...
console.log(error);
});
const newEvent = await getDoc(newEventRef);
return newEvent;
}
SOLUTION updated from scra:
export async function addEvent(event) {
try {
const eventsRef = collection(db, EVENTS_COLLECTION);
const docRef = await addDoc(eventsRef, event);
console.log("Event is added, id :", docRef.id);
} catch (error) {
console.error(error);
}
}
const handleDatePicked = (date) => {
const formattedDate = date.toLocaleDateString();
const formattedTime = date.toLocaleTimeString();
setDate(`${formattedDate} ${formattedTime}`);
setDateTimePickerVisible(false);
};
const handleSubmit = async () => {
try {
await addEvent({ ...event, date });
} catch (error) {
console.error(error);
}
};
return (
<View>
<TextInput
onChangeText={(text) => setEvent({ ...event, name: text })}
placeholder="Event name"
/>
<Text>{date}</Text>
<Button
Color="red"
title="Pick date and time"
onPress={showDateTimePicker}
/>
<DateTimePicker
mode="datetime"
isVisible={isDateTimePickerVisible}
onConfirm={handleDatePicked}
onCancel={() => setDateTimePickerVisible(false)}
/>
<Button title="Submit" onPress={handleSubmit} />
</View>
);
The error indicates that you try to write a document with an unsupported field value for the field date.
You will find here the list of the data types supported by Firestore. You need to transform the Object returned by your date picker to an Object that has a type supported by Firestore.

Firefox: React app not populating root element with weird error

I have a react application built for production and being served by a node/express server.
I have started the server locally and also deployed it to heroku and render.com
On all these systems, Chrome and Edge have no problems and no errors when accessing the site. But: Firefox won't populate the root element and shows a weird error. Only after refreshing, Firefox shows the site perfectly.
This is the error and the corresponding code ... it doesn't make sense
TypeError: wi.get(...) is undefined
ts Header.jsx:36
React 7
C scheduler.production.min.js:13
T scheduler.production.min.js:14
813 scheduler.production.min.js:14
Webpack 12
react-dom.production.min.js:189:29
React 9
C scheduler.production.min.js:13
T scheduler.production.min.js:14
(Async: EventHandlerNonNull)
813 scheduler.production.min.js:14
Webpack 12
const availableLanguages = [
{
code: "he",
name: "עברית",
country_code: "il",
dir: "rtl",
},
{
code: "en",
name: "English",
country_code: "gb",
},
{
code: "de",
name: "Deutsch",
^---- The error is showing for this code position! There is no code!
country_code: "de",
},
];
This happens on all three environments, tested from three different systems.
Does anyone know what is happening?
EDIT: Full Header.jsx
import { useState, useRef, useEffect } from "react";
import i18next from "i18next";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import useOutsideClick from "../hooks/useOutsideClick";
import useAuth from "../hooks/useAuth";
import Anchor from "./Anchor";
import "./Header.css";
import claryNextLogo from "../images/ClaryNext2.png";
import cookies from "js-cookie";
import { config } from "../Environment";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { solid } from "#fortawesome/fontawesome-svg-core/import.macro";
import { useContext } from "react";
import { WSContext } from "../App";
import { useEffectOnce } from "../hooks/useEffectOnce";
import { distributionService } from "../services/distributionService";
import MessageBox from "./MessageBox";
import useRetryFetch from "../hooks/useRetryFetch";
const availableLanguages = [
{
code: "he",
name: "עברית",
country_code: "il",
dir: "rtl",
},
{
code: "en",
name: "English",
country_code: "gb",
},
{
code: "de",
name: "Deutsch",
country_code: "de",
},
];
function Header() {
const currentLanguageCode = cookies.get("i18next").split("-")[0] || "en";
const currentLanguage = availableLanguages.find(
(l) => l.code === currentLanguageCode
);
const { auth, setAuth } = useAuth();
const navigate = useNavigate();
const dropdownRef = useRef(null);
const retryFetch = useRetryFetch();
const [showLanguageDropdown, setShowLanguageDropdown] = useState(false);
const [showMessageBox, setShowMessageBox] = useState(false);
const [msgBoxTitle, setMsgBoxTitle] = useState("");
const [msgBoxButtons, setMsgBoxButtons] = useState({});
const [msgBoxInputs, setMsgBoxInputs] = useState([]);
const [msgBoxId, setMsgBoxId] = useState("");
const [msgBoxMoreJSX, setMsgBoxMoreJSX] = useState(null);
const [chatRequestUser, setChatRequestUser] = useState("");
const [chatRequestProcessId, setChatRequestProcessId] = useState("");
const [chatRequestRoomId, setChatRequestRoomId] = useState(0);
const { t } = useTranslation();
const { socket } = useContext(WSContext);
const openMessageBox = (title, id, buttons, inputs, moreJSX) => {
setMsgBoxTitle(title);
setMsgBoxId(id);
setMsgBoxButtons(buttons);
setMsgBoxInputs(inputs);
setMsgBoxMoreJSX(moreJSX);
setShowMessageBox(true);
};
const onButton = async (result) => {
console.log("MessageBox button was clicked");
console.dir(result);
if (result.btnId === "yes") {
// chat was accepted, change to ShowFullLog, join room and notify user
socket.send(
JSON.stringify({
command: "acceptchat",
payload: { email: chatRequestUser, roomId: chatRequestRoomId },
})
);
// collect necessary process log information
let response = await retryFetch(
`${config.API_BASE}/api/processes/${chatRequestProcessId}`
);
let process = await response.json();
let stateToPass = {
processId: chatRequestProcessId,
presName: process.prescriptionName,
presHistoryId: process.fiPrescriptionHistory._id,
autochat: true,
};
if (process.fiExpert?.email === auth.email) {
stateToPass.userEmail = process.fiInitiator?.email;
navigate("/fulllog", { state: stateToPass });
} else {
stateToPass.expertEmail = process.fiExpert?.email;
navigate("/userlog", { state: stateToPass });
}
} else {
// chat was refused, send message to requesting user
socket.send(
JSON.stringify({
command: "refusechat",
payload: {
email: chatRequestUser,
roomId: chatRequestRoomId,
},
})
);
}
};
useEffect(() => {
document.body.dir = currentLanguage?.dir || "ltr";
}, [currentLanguage]);
useEffectOnce(() => {
let messageUnsubscribe = distributionService
.getMessage()
.subscribe((msg) => {
console.log("Header incoming message");
switch (msg.command) {
case "requestchat":
setChatRequestUser(msg.payload.email);
setChatRequestProcessId(msg.payload.processId);
setChatRequestRoomId(msg.payload.roomId);
openMessageBox(
t("msg_chat_requested", {
user: msg.payload.email,
processId: msg.payload.processId,
}),
"requestchat",
[
{ id: "yes", text: t("yes") },
{ id: "no", text: t("no") },
],
[],
""
);
break;
}
});
return () => {
// cleanup subscription
messageUnsubscribe.unsubscribe();
};
}, []);
const login = () => {
navigate("/login");
};
const logout = async () => {
let response = await fetch(config.API_BASE + "/logout", {
credentials: "include",
});
if (!response.ok) alert(t("msg_error_logout"));
let result = await response.json();
console.log(result);
setAuth({});
socket.send(
JSON.stringify({
command: "logout",
payload: undefined,
})
);
navigate("/");
};
// const register = () => {
// navigate("/register");
// };
const showPrescriptions = () => {
//navigate("/design");
navigate("/prescriptionlist");
};
const goHome = () => {
navigate("/");
};
const openMenu = () => {
setShowLanguageDropdown(true);
};
const closeMenu = () => {
setShowLanguageDropdown(false);
};
const selectLanguage = (code) => {
i18next.changeLanguage(code);
setShowLanguageDropdown(false);
};
useOutsideClick(dropdownRef, closeMenu);
return (
<>
<div className="header-menu">
<div className="title" onClick={goHome}>
<img src={claryNextLogo} alt="ClaryNext logo" width="90" />
</div>
{!auth?.email ? (
<>
<div className="notonmobile">
<div>
<Anchor
onClick={showPrescriptions}
text={t("example_prescriptions")}
/>
</div>
</div>
<div className="rightflex notonmobile">
<div className="rightMargin">
<span> </span>
</div>
<button onClick={login}>{t("login")}</button>
<span className="leftMargin bigger">
<FontAwesomeIcon
onClick={() => openMenu()}
icon={solid("globe")}
/>
</span>
</div>
</>
) : (
<>
<div className="rightMargin notonmobile">
<Anchor
onClick={showPrescriptions}
text={t("prescriptions_and_processes")}
/>
</div>
<div className="rightflex notonmobile">
<div className="rightMargin">
<Anchor onClick={logout} text={t("logout")} />
</div>
<span className="smaller">{auth.email}</span>
<span className="leftMargin bigger">
<FontAwesomeIcon
onClick={() => openMenu()}
icon={solid("globe")}
/>
</span>
</div>
</>
)}
</div>
{showMessageBox ? (
<MessageBox
onButton={onButton}
buttons={msgBoxButtons}
text={msgBoxTitle}
inputs={msgBoxInputs}
id={msgBoxId}
moreJSX={msgBoxMoreJSX}
onClose={() => setShowMessageBox(false)}
/>
) : (
""
)}
{showLanguageDropdown ? (
<div className="language_dropdown" ref={dropdownRef}>
{availableLanguages.map((lang, idx) => (
<p key={idx} onClick={() => selectLanguage(lang.code)}>
<span
className={`flag-icon flag-icon-${lang.country_code}`}
></span>
{lang.name}
</p>
))}
</div>
) : (
""
)}
</>
);
}
export default Header;
Originally, I had the code to access the cookie in the main function scope. After I moved it into the useEffect[] callback, it started working also in Firefox.

Why do I have to refresh the page when I delete a post? MERN stack

I am a beginner in the MERN stack and I am interested in why I have to refresh the page after deleting the document (post)?
This is my Action.js
export const deletePost = id => async (dispatch, getState) => {
try {
dispatch({ type: DELETE_POST_BEGIN });
const {
userLogin: { userInfo },
} = getState();
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.delete(`/api/v1/post/${id}`, config);
dispatch({ type: DELETE_POST_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: DELETE_POST_FAIL,
payload: { msg: error.response.data.msg },
});
}
};
This is my Reducer.js
export const deletePostReducer = (state = {}, action) => {
switch (action.type) {
case DELETE_POST_BEGIN:
return { loading: true };
case DELETE_POST_SUCCESS:
return { loading: false };
case DELETE_POST_FAIL:
return { loading: false, error: action.payload.msg };
default:
return state;
}
};
And this is my Home page where i list all posts:
import { useEffect } from 'react';
import { Col, Container, Row } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { getPosts } from '../actions/postActions';
import Loader from '../components/Loader';
import Message from '../components/Message';
import Post from '../components/Post';
const HomePage = () => {
const dispatch = useDispatch();
const allPosts = useSelector(state => state.getPosts);
const { loading, error, posts } = allPosts;
const deletePost = useSelector(state => state.deletePost);
const { loading: loadingDelete } = deletePost;
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
return (
<Container>
{loading || loadingDelete ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<>
<Row>
{posts.map(post => (
<Col lg={4} key={post._id} className='mb-3'>
<Post post={post} />
</Col>
))}
</Row>
</>
)}
</Container>
);
};
export default HomePage;
And this is my single Post component:
const Post = ({ post }) => {
const dispatch = useDispatch();
const allPosts = useSelector(state => state.getPosts);
const { loading, error, posts } = allPosts;
const userLogin = useSelector(state => state.userLogin);
const { userInfo } = userLogin;
const handleDelete = id => {
dispatch(deletePost(id));
};
return (
<>
<div>{post.author.username}</div>
<Card>
<Card.Img variant='top' />
<Card.Body>
<Card.Title>{post.title}</Card.Title>
<Card.Text>{post.content}</Card.Text>
<Button variant='primary'>Read more</Button>
{userInfo?.user._id == post.author._id && (
<Button variant='danger' onClick={() => handleDelete(post._id)}>
Delete
</Button>
)}
</Card.Body>
</Card>
</>
);
};
And my controller:
const deletePost = async (req, res) => {
const postId = req.params.id;
const post = await Post.findOne({ _id: postId });
if (!post.author.equals(req.user.userId)) {
throw new BadRequestError('You have no permission to do that');
}
await Post.deleteOne(post);
res.status(StatusCodes.NO_CONTENT).json({
post,
});
};
I wish someone could help me solve this problem, it is certainly something simple but I am a beginner and I am trying to understand.
I believe the issue is that you are not fetching the posts after delete is successful.
Try this inside the HomePage component:
...
const [isDeleting, setIsDeleting] = useState(false);
const { loading: loadingDelete, error: deleteError } = deletePost;
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
useEffect(() => {
if (!deleteError && isDeleting && !loadingDelete) {
dispatch(getPosts());
}
setIsDeleting(loadingDelete);
}, [dispatch, deleteError, isDeleting, loadingDelete]);
...
Another method is to use "filtering", but you have to update your reducer as such:
export const deletePostReducer = (state = {}, action) => {
switch (action.type) {
case DELETE_POST_BEGIN:
return { loading: true };
case DELETE_POST_SUCCESS:
return { loading: false, data: action.payload}; // <-- this was changed
case DELETE_POST_FAIL:
return { loading: false, error: action.payload.msg };
default:
return state;
}
};
Now in your HomePage component, you will do something like this when rendering:
...
const { loading: loadingDelete, data: deletedPost } = deletePost;
...
useEffect(() => {
dispatch(getPosts());
if (deletedPost) {
console.log(deletedPost);
}
}, [dispatch, deletedPost]);
return (
...
<Row>
{posts.filter(post => post._id !== deletedPost?._id).map(post => (
<Col lg={4} key={post._id} className='mb-3'>
<Post post={post} />
</Col>
))}
</Row>
)

ReactJS: Is there a way to avoid child component re-rendering when drawer opens?

I'm looking for a way to be able to open the drawer, with the least possible re-rendering of the DOM.
Every time the drawer opens, a re-rendering of the route component occurs, however I think it is unnecessary since the child components have not changed and are only re-rendered due to the fact that I am updating the state to open the drawer.
Aprecio su ayuda y orientacion con este tema
This is my code for Appbar.js
import React, { useState, Fragment, lazy, Suspense, useCallback} from "react";
import { Switch,Router, Route, Link, useLocation } from "react-router-dom";
import { createBrowserHistory } from "history";
import { withStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import logo_LH_webp from '../../imagenes/logo.webp';
import logo_LH_png from '../../imagenes/logo.png';
import AccountCircle from '#material-ui/icons/AccountCircle';
import { useSelector } from 'react-redux'
import { isEmpty } from 'react-redux-firebase'
import { useFirebase } from "react-redux-firebase";
import { store } from '../../index'
import { setMesa } from "../../slices/cart";
import axios from 'axios';
import {MemoMyDrawer} from './MyDrawer'
import Button from '#material-ui/core/Button';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import PopupState, { bindTrigger, bindMenu } from 'material-ui-popup-state';
const drawerWidth = 240;
const history = createBrowserHistory();
const styles = theme => ({
root: {
flexGrow: 1
},
flex: {
flex: 1
},
drawerPaper: {
position: "relative",
width: drawerWidth
},
menuButton: {
marginLeft: "auto",
},
toolbarMargin: theme.mixins.toolbar,
aboveDrawer: {
zIndex: theme.zIndex.drawer + 1
},
appBar: {
backgroundColor: "white"
}
});
let vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
const menuId = 'primary-search-account-menu';
const getQuery = () => {
if (typeof window !== "undefined") {
return new URLSearchParams(window.location.search);
}
return new URLSearchParams();
};
function AppBarInteraction({ classes, variant }) {
const query = getQuery();
console.log("MESA")
console.log()
const cart = useSelector(state => state.cart);
let valor = getQuery().get('q');
if (cart.mesa === "")
{
store.dispatch(setMesa(valor));
}
else
{
valor = cart.mesa;
}
const auth = useSelector(state => state.firebase.auth);
console.log('Auth vacio')
console.log(isEmpty(auth))
console.log(auth.isEmpty);
const [drawer, setDrawer] = useState(false);
const firebase = useFirebase();
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
console.log("usuario");
console.log(user);
//setAutorizado(true);
const token = await user.getIdToken();
axios.defaults.headers.common['Authorization'] = "Bearer " + token;
} else {
console.log("No logueado");
//setAutorizado(false);
}
});
// const [autorizado, setAutorizado] = React.useState(null);
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
console.log('Autorizado?');
//console.log(autorizado);
const toggleDrawer = useCallback(() => {
console.log(drawer)
setDrawer(!drawer);
}, [drawer]);
const logout = () => {
//setAutorizado(false);
firebase.logout();
}
const perfil = () => {
history.push("/perfil");
}
const MemoMyDraw = (drawer) => (
<MemoMyDrawer
open={drawer}
variant="temporary"
onClose={toggleDrawer}
toggleDrawer={toggleDrawer}
/>
)
const MenuPropio = () => (
// <Menu
// id="menu-appbar"
// anchorEl={anchorEl}
// open={open}
// onClose={handleClose}
// >
// <MenuItem onClick={perfil}>Perfil de Usuario</MenuItem>
// <MenuItem onClick={logout}>Cerrar Sesión</MenuItem>
// </Menu>
<PopupState variant="popover" popupId="demo-popup-menu">
{(popupState) => (
<React.Fragment>
<IconButton
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
color="primary"
{...bindTrigger(popupState)}
>
<AccountCircle />
</IconButton>
<Menu {...bindMenu(popupState)}>
<MenuItem onClick={perfil}>Perfil de Usuario</MenuItem>
<MenuItem onClick={logout}>Cerrar Sesión</MenuItem>
</Menu>
</React.Fragment>
)}
</PopupState>
)
return (
<div className={classes.root}>
<Fragment>
<Router history={history}>
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<Link to={"/" + "?q=" + valor} ><picture><source srcSet={logo_LH_webp} type="image/webp" /><img alt="logo" srcSet={logo_LH_png} height="56" width="77" /></picture></Link>
<IconButton
color="default"
aria-label="open drawer"
edge="end"
onClick={toggleDrawer} //{handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
{true && (
<Fragment>
<div>
<MenuPropio />
</div>
</Fragment>
)}
</Toolbar>
</AppBar>
</Router>
<div className={classes.toolbarMargin} />
</Fragment>
{MemoMyDraw(drawer)}
</div>
);
}
export default React.memo(withStyles(styles)(AppBarInteraction));
And this is the code for MyDrawer.js
import React, { Suspense, lazy} from "react";
import { withStyles } from "#material-ui/core/styles";
import Drawer from "#material-ui/core/Drawer";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import PrivateRoute from "../PrivateRoute/PrivateRoute"
import clsx from "clsx";
import Divider from "#material-ui/core/Divider";
import { Switch,Router, Route, Link, useLocation } from "react-router-dom";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import InboxIcon from "#material-ui/icons/MoveToInbox";
import MailIcon from "#material-ui/icons/Mail";
import { createBrowserHistory } from "history";
import { useSelector } from 'react-redux'
import { isLoaded, isEmpty } from 'react-redux-firebase'
import Prueba from '../Prueba/Prueba';
const Cart = lazy(() => import ('../Cart/Cart'));
const Feedback = lazy(() => import ('../Feedback/Feedback'));
const Perfil = lazy(() => import ('../Perfil/Perfil'));
const OneClick = lazy(() => import ('../OneClick/OneClick'));
const Login = lazy(() => import ('../Login/Login'));
const Menu_LH = lazy(() => import ('../Menu/Menu'));
const Principal = lazy(() => import('../Principal/Principal'));
const ProductList = lazy(() => import( '../ProductList/ProductList'));
const Carta= lazy(() => import( '../Carta/Carta'));
const ProductosCategoria = lazy(() => import('../ProductosCategoria/ProductosCategoria'));
const InfoProducto = lazy(() => import('../InfoProducto/InfoProducto'));
const Confirmacion = lazy(() => import('../Confirmacion/Confirmacion'));
const drawerWidth = 240;
const history = createBrowserHistory();
function AuthIsLoaded({ children }) {
const auth = useSelector(state => state.firebase.auth)
if (!isLoaded(auth)) return <div></div>;
return children
}
console.log("DRAWER----------------------------")
const getQuery = () => {
if (typeof window !== "undefined") {
return new URLSearchParams(window.location.search);
}
return new URLSearchParams();
};
function switchResult(a){
switch(a){
case 'Tu Pedido Actual':
return 'pedido';
break;
case 'Carta':
return 'carta';
break;
case 'Inicio':
return '';
break;
case 'Carta':
return 'Carta';
break;
case 'Prueba':
return 'Prueba';
break;
case 'Comentarios':
return 'feedback';
break;
default:
return "OK";
}
}
const styles = theme => ({
root: {
flexGrow: 1
},
flex: {
flex: 1
},
drawerPaper: {
position: "relative",
width: drawerWidth
},
menuButton: {
marginLeft: "auto",
},
toolbarMargin: theme.mixins.toolbar,
aboveDrawer: {
zIndex: theme.zIndex.drawer + 1
},
appBar: {
backgroundColor: "white"
}
});
const MyDrawer = withStyles(styles)(
({ classes, variant, open, onClose, toggleDrawer }) => (
<Router history={history}>
<Drawer
variant="temporary"
open={open}
onClose={onClose}
classes={{
paper: classes.drawerPaper
}}
>
<div
className={clsx({
[classes.toolbarMargin]: variant === "persistent"
})}
/>
<Divider />
<List>
{["Inicio", "Carta" , "Tu Pedido Actual", "Comentarios" ].map((text, index) => (
<ListItem key={text} onClick={toggleDrawer} component={Link} to={"/" + switchResult(text) + "?q=" + getQuery().get('q')}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
<main className={classes.content}>
<Suspense fallback={<div>Cargando...</div>}>
<Switch>
<AuthIsLoaded>
<PrivateRoute exact path = "/" component={Principal}></PrivateRoute>
<PrivateRoute exact path="/perfil" component={Perfil}></PrivateRoute>
<PrivateRoute exact path="/OneClick"component={OneClick}></PrivateRoute>
{/* <Route exact path="/Menu" render={() => <div id="G"><ProductList /></div>} /> */}
<PrivateRoute path="/carta"component={Carta}></PrivateRoute>
<PrivateRoute path="/pedido"component={Cart}></PrivateRoute>
{/* <Route path="/carta" render={() => <div id="G"><ProductList /></div>} /> */}
{/* <PrivateRoute exact path="/"><div id="G"><Principal /></div></PrivateRoute> */}
<Route exact path="/Categoria/:categoriaId"><div id="ProductosCategoria"><ProductosCategoria /></div></Route>
<Route exact path="/login"><div id="Login"><Login /></div></Route>
<Route exact path="/feedback" > <div id="Feedback"><Feedback /></div></Route>
</AuthIsLoaded>
</Switch>
</Suspense>
</main>
</Router>
)
);
export const MemoMyDrawer = React.memo(withStyles(styles)(MyDrawer));
You have to separate the reference to drawer's state open from MyDrawer because when open chage re-render MyDrawer. My solution was to extends Drawer and manage its state with redux using this code:
import { useSelector, useDispatch } from "react-redux";
function VDrawer(props){
const dispatch = useDispatch();
const onClose=() => {dispatch({ type: "drawer"})};
const drawer = useSelector((state) => state.drawer);
return <Drawer variant="temporary"
open={drawer}
onClose={onClose}
ModalProps={{
keepMounted: true,
}}>
{props.children}
</Drawer>
}
In this way the state drawer wich open/close drawer don't affect to MyDrawer

React Hooks - How to pass props from child to parent component

In the bellow example, how can I pass imageAsUrl.imgUrl from the Child component (ImageUpload) to the parent component (UpdateCard).
CHILD Component
import React, { useState, useEffect } from 'react';
import { storage } from '../firebase';
const ImageUpload = () => {
const allInputs = { imgUrl: '' };
const [imageAsUrl, setImageAsUrl] = useState(allInputs);
const [image, setImage] = useState(null);
const handleChange = (e) => {
if (e.target.files[0]) {
setImage(e.target.files[0]);
}
};
useEffect(() => {
if (image) {
const uploadTask = storage.ref(`images/${image.name}`).put(image);
uploadTask.on(
'state_changed',
(snapshot) => {},
(error) => {
console.log(error);
},
() => {
storage
.ref('images')
.child(image.name)
.getDownloadURL()
.then((fireBaseUrl) => {
setImageAsUrl((prevObject) => ({
...prevObject,
imgUrl: fireBaseUrl,
}));
});
}
);
}
}, [image]);
return (
<>
<label className='custom-file-upload'>
<input type='file' onChange={handleChange} />
</label>
<img src={imageAsUrl.imgUrl} alt='sample' />
</>
);
};
export default ImageUpload;
PARENT Component
import React, { useState } from 'react';
import firebase from '../firebase';
import ImageUpload from './ImageUpload';
const UpdateCard = ({ card }) => {
const [originalText, setOriginalText] = useState(card.originalText);
const [translatedText, setTranslatedText] = useState(card.translatedText);
const onUpdate = () => {
const db = firebase.firestore();
db.collection('FlashCards')
.doc(card.id)
.set({ ...card, originalText, translatedText });
timeOutScroll();
};
return (
<>
<div>
{card.imageURL ? (
<img src={card.imageURL} alt='' className='img' />
) : (
<textarea
className='upload-textarea'
value={originalText}
onChange={(e) => {
setOriginalText(e.target.value);
}}
/>
)}
<ImageUpload />
</div>
<textarea
value={translatedText}
onChange={(e) => {
setTranslatedText(e.target.value);
}}
/>
<button onClick={onUpdate}>Update</button>
</>
);
};
export default UpdateCard;
Inside parent,You can define a callback function as prop ref to be called inside the child.
const ImageUpload = ({getURLtoParent}) =>{ <--------------------
const [imageAsUrl, setImageAsUrl] = useState(allInputs);
useEffect(() => {
uploadTask.on(
..............
...
);
if(imageAsUrl.imgUrl !== '')
getURLtoParent(imageAsUrl.imgUrl) <-----------------------
},[image])
}
const UpdateCart = () => {
const[imgURL,setimgURL] = useState(null)
return (
......
<ImageUpload getURLtoParent={ (url) => setimgURL(url) } /> <----------------
.........
)
}

Resources