I have an item and when I click on more info button a modal window with description appears but there is no endpoint like /modal in current route cause it's not external page I redirect to.
So in my modal window I have make a bid button and I can set a price in case I'm logged in.
otherwise Modal window with login form should appear with a request to log in.
This modal is on my navBar that is fixed whether I'm on current page or another one.
So how to pass this Modal Log in using function from another component ?
Here is my Modal with item description:
const ModalDetails = (props) => {
console.log(props, ' for modal')
const [loggedIn, setLoggedIn] = useState(false)
const checkUser = () => {
if (!loggedIn) {
/// how to pass that path to log in modal??
}
}
return (
{ item.typeSale == 'auction' && <button className='btn-item auction-btn-bet'
onClick={checkUser}>Make a bet</button>}
)
}
Log in modal in my App.js
const App = () => {
...
<Nav.Link className=' nav-item link-nav button-nav' onClick={handleShow}>
<img className='img-small' src={person} />
Log in
</Nav.Link>
<LoginForm show={show} handleShow={handleShow} handleClose={handleClose} />
}
I have everything as a separate component
I don't think it is possible. I think you can use Redux or Context to store the modal's open state.
Related
The site has a button for deleting records (DeleteForeverIcon in the code). When you click on this button, a window opens (made according to the documentation using Dialog Mui).
When you click on the "Confirm" button, the handleDeleteItem() function is launched, which deletes the entry. But I can’t understand why the window closes while this function is running, because I don’t switch the state anywhere
Looking for a solution on my own, I added console.log() to my code (below is the same code, only with console.log()) and came up with the following conclusion: when I run the handleDeleteItem() function, the open state switches to false and so the window closes. But why does it switch to false?
export function DeleteButton({ item }) {
const { urlParams } = useContext(PageContext)
const { firestore } = useContext(AppContext)
const [open, setOpen] = useState(false);
console.log(open, "window close") // console.log here
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleDeleteItem = async () => {
console.log("start") // console.log here
await deleteItem()
console.log("finish") // console.log here
}
return (
<ButtonGroup>
<DeleteForeverIcon onClick={handleClickOpen}/>
<Dialog
open={open}
onClose={handleClose}>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleDeleteItem}>Confirm</Button>
</DialogActions>
</Dialog>
</ButtonGroup >
)
}
That is, summing up the question, I would like the window to close only after the deleteItem () function has completed, and not during its execution
Based on some further clarification in the comments, it seems as though your issue is to do with the fact that calling deleteItem(...) causes your state to update in your parent components (due to an onSnapshot firebase listener). Your parent components are responsible for rendering the children components. When your state updates, the item/row that you deleted won't be in the new state value, and so the component that was rendering your Dialog previously won't be rendered (because it has been deleted).
Here is a minified example of your issue:
const { useState } = React;
const List = () => {
const [items, setItems] = useState(["a", "b", "c", "d", "e"]);
const handleDelete = (charToDel) => {
setItems(items => items.filter(char => char !== charToDel));
}
return <ul>
{items.map(char =>
<li key={char}>{char} - <DeleteButton value={char} onDelete={handleDelete}/></li>
)}
</ul>
}
const DeleteButton = ({value, onDelete}) => {
const [open, setOpen] = useState(false);
return <React.Fragment>
<button onClick={() => setOpen(true)}>×</button>
<dialog open={open}>
<p>Delete {value}?</p>
<button onClick={() => onDelete(value)}>Confirm</button>
</dialog>
</React.Fragment>
}
ReactDOM.createRoot(document.body).render(<List />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
So since you're not rendering the component that renders the Dialog once you remove the item, you won't be rendering the <Dialog> anymore and so it disappears.
One way to fix this is to lift the <Dialog> component up to a component that doesn't get removed when you remove an item from your state. By the looks of things, the closest parent component that has this property is DevicesTable. In there you can render your dialog and keep track of a selectedItem to determine which item that should be deleted, which you can set based on the item you press (see code comments below):
// DevicesTable component
const [selectedItem, setSelectedItem] = useState();
const handleClose = () => {
setSelectedItem(null);
}
const handleDeleteItem = () => { // this doesn't need to be `async` if not using `await`
deleteItem(selectedItem, firestore, urlParams); // this doesn't need to be `await`ed if you don't have any code following it
}
return (
<>
{/* vvvvvv -- move dialog here -- vvvvvv */}
<Dialog open={!!selectedItem} onClose={handleClose}>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleDeleteItem}>Confirm</Button>
</DialogActions>
</Dialog>
{/* ^^^^^^ -- move dialog here -- ^^^^^^ */}
<TableContainer className="TableContainerGridStyle">
<Table className="TableStyle">
<DevicesTableHeader />
<TableBody className="TableBodyStyle">
{devices.map((device) =>
<DevicesTableCell
device={device}
onDeleteButtonPress={() => setSelectedItem(device)} /* <--- set the selected item */
key={device.description.id}
/>
)}
</TableBody>
</Table>
</TableContainer>
</>
);
For brevity, I've removed the open state and instead used the presence of the selectedItem to determine if the modal should be open or not, but you can of course add that back in if you wish and set both the selectedItem and the open state when opening and closing the modal.
Within DevicesTableCell, you would then grab the onDeleteButtonPress prop, and then pass it to DeleteButton like so:
// v-- grab the function
function DevicesTableCell({ device, onDeleteButtonPress }) {
...
<DeleteButton item={device} onDeleteButtonPress={onDeleteButtonPress}/> {/* pass it to the componennt */}
...
}
Within DeleteButton you should then invoke the onDeleteButtonPress function:
<DeleteForeverIcon onClick={onDeleteButtonPress} />
If you don't like the idea of passing callbacks down through multiple components like this, you can avoid that by using useReducer with a context, as described here.
I have this app, where i want to display an logout button in the navigation when the user is logged in. But for some reason, the button only shows after i do a refresh of the page, not when the user is logged in.
Also the same thing happens for the cart number update (on the same component), it only update the number after a refresh. Here i am trying to get number from local storage. I made a seperat component for the "buy" button since i want it on all items.
I need some help please, what am i doing wrong?
Here is Navigation component:
function Navigation() {
const [loggedIn, setLoggedIn] = useState(JSON.parse(localStorage.getItem("games")));
let history = useHistory();
function logOut() {
localStorage.clear('Email', 'Password');
history.push('/')
}
function getCartItems() {
if (localStorage.getItem("games")) {
return JSON.parse(localStorage.getItem("games")).length
}
else {
return 0;
}
};
return (
<div className="navgation_container">
<div className="navigation_cartcontainer">
<img className="navigation_logo" src={logo} alt="logog bits&bots" />
{loggedIn ? (
<div className="navigation_cartimg">
<img className="navigation_cart" src={cart} alt="cart for the page" />
<p className="navigation_numberitems">{getCartItems()}</p>
<button className="navigation_logout" onClick={logOut}>Log out</button>
</div>
) : (
<div>Test</div>
)}
</div>
</div>
);
}
Using useState might help you and pass in the value from local storage to useState as shown :
useEffect( ()=>{
setLoggedIn(data_from_local_storage);
}[data_from_local_storage]);
// Hi i am a creating a simple chat Application. In this component ,I am Showing All the messages between users .Everything is working as expected .Now i want when a user receives or sends new message i want it should auto scroll to the last message. Now I want to add this feature
import React from "react";
import { Link } from "react-router-dom";
import { useSelector } from "react-redux";
function ShowMessages() {
const currentUserName = useSelector(
(state) => state.user.currentUser.username
);
const allmessages = useSelector((state) => state.message.allMessage);
const chatWith = useSelector((state) => state.user.chatWith);
const LoggedInUser = currentUserName && currentUserName.split("#")[0];
return (
<>
{allmessages &&
allmessages
.filter((item) => {
console.log("item", item.from, item.to, chatWith.id);
return item.fromto === chatWith.id;
})
.map((item, idx) => (
<li
className={item.direction === "send" ? "replies" : "sent"}
key={idx}
>
<div className="media">
<h5>
{item.messageBody}
</h5>
</div>
</li>
))}
</>
);
}
export default ShowMessages;
Right now you have a component ShowMessages, which renders messages, but in your snippet i can't see any container (perhaps you have it a different component). What i would do is put a ref (by using "useRef") on the container, which holds all of the messages.
And every time any message is added (this could be tracked via useEffect), you can get access to the container via the ref (don't forget to use .current). Then, you can choose how to scroll down from this thread - Scroll to bottom of div?
First answer should work just fine. Just remember, that you get the element by the ref!
I use react in the project. The modal closes when the user refreshes, but I want to modify it so that it doesn't close. I found 'beforeunload' event, but I want to prevent 'alert' from appearing.
And when I refresh, I want to keep the data I received from the API and maintain the model.
I want to make the modal close only when I press the close button, what methods can I use?
This is not a real code. Just a example...
function Modal() {
const [open, setOpen] = useState(false)
const handleOpen = () => {
setOpen(!open);
}
return (
<Modal>
<button onClick={handleOpen}>
modal
</button>
(some data from API)
</Modal>
)
}
Read localStorage value first, if it exists use it, and then everytime you set a new value update it in local storage as well.
Remember localstorage only saves values as a string; and when it doesn't exist it will come back as a null.
function Modal() {
const [open, setOpen] = useState(JSON.parse(localStorage.getItem("open")) || false)
const handleOpen = () => {
setOpen((prevState) => {
localStorage.setItem("open", !prevState);
return !prevState;
});
}
return (
<Modal>
<button onClick={handleOpen}>
modal
</button>
</Modal>
)
}
I am trying to move the open state for material-ui dialog to redux to prevent it from closing when a rerender occurs, but i having trouble with the dialog when a rerender occurs. Although the state is saved in redux and the dialog does stay open whenever a rerender occurs the open state stays open but the dialog does show the open animation (fading in) which is kinda annoying.
Students.js (parent component of the modal)
const Students = ({
app: { studentsPage: { savedAddDialogOpen }},
setStudentsPageAddDialogOpen}) => {
// Create the local states
const [dialogOpen, setDialogOpen] = React.useState(savedAddDialogOpen);
const dialogOpenRef = React.useRef(savedAddDialogOpen);
// Change redux dialog open
const setReduxDialogState = () => {
setStudentsPageAddDialogOpen(dialogOpenRef.current, savedAddDialogOpen);
};
// Open add student dialog
const dialogClickOpen = () => {
setDialogOpen(true);
dialogOpenRef.current = true;
setTimeout(() => setReduxDialogState(), 300);
};
// Close add student dialog
const dialogClose = () => {
setDialogOpen(false);
dialogOpenRef.current = false;
setTimeout(() => setReduxDialogState(), 300);
};
return (
<Container>
{/* Add student modal */}
<AddStudentModal dialogOpen={dialogOpen} dialogClose={dialogClose} />
</Container>
)
}
// Set the state for this component to the global state
const mapStateToProps = (state) => ({
app: state.app,
});
AddStudentModal.js
const AddStudentModal = ({
dialogOpen, dialogClose
}) => {
return (
<Dialog
open={dialogOpen}
>
{/* Lots of stuff*/}
<DialogActions>
<Button onClick={dialogClose}>
Close dialog
</Button>
</DialogActions>
</Dialog>
)
};
I hope this should be sufficient. I tried checking if the open state is actually correct when a rerender occurs and it is correct every time but it looks like the dialog is closed at a rerender no matter what the open state is and only a few ms later actually notices that it should be opened.
Any help would be really appreciated
Edit 1: Found out it has nothing to do with the open state coming from redux, if i use open={true} it still flashes, so probably a problem with material-ui itself?
Edit 2: PrivateRoute.js
const PrivateRoute = ({
auth: { isAuthenticated, loadingAuth },
user: { loggedInUser },
component: Component,
roles,
path,
setLastPrivatePath,
...rest
}) => {
useEffect(() => {
if (path !== '/dashboard' && path !== '/profile') {
setLastPrivatePath(path);
}
// Prevent any useless errors with net line:
// eslint-disable-next-line
}, [path]);
// If we are loading the user show the preloader
if (loadingAuth) {
return <Preloader />;
}
// Return the component (depending on authentication)
return (
<Route
{...rest}
render={props =>
!isAuthenticated ? (
<Redirect to="/login" />
) : (loggedInUser && roles.some(r => loggedInUser.roles.includes(r))) ||
roles.includes('any') ? (
<Component {...props} />
) : (
<NotAuthorized />
)
}
/>
);
};
// Set the state for this component to the global state
const mapStateToProps = state => ({
auth: state.auth,
user: state.user
});
I found the problem thanks to #RyanCogswell!
For anyone having the same problem here is the cause for me and the fix:
I was passing components into the Route component like this:
<PrivateRoute
exact
path={'/dashboard/students'}
component={(props) => (
<Students {...props} selectedIndex={selectedIndexSecondary} />
)}
roles={['admin']}
/>
By doing it this way i could pass props through my privateRoute function but this would also happen if you send the component this way in a normal Route component
Solution for me is just moving selectedIndexSecondary to redux and sending the component the normal way it prevented the re-mounting.
So just doing it like this will prevent this from happening.
<PrivateRoute
exact
path={'/dashboard/students'}
component={Students}
roles={['admin']}
/>
Also this will solve the localstates in your components from resseting to the default value. So for me it fixed two problems!