Delete images if the user is leaving the component React - javascript

I have form with a drag and drop component where I can upload images, after this I send these pictures with axios to my backend and save it on server side and then re-render it in a preview mode. My problem is, that if a user uploads some pictures and after that he switches to another page without submitting the form the added pictures are staying on my server side for no reason.
So the question: I want to check inside my component if a user is leaving, show a prompt and if he clicks on the OK button I want to delete all the added pictures from my backend before leaving. How can I detect this?
My simplified snippet:
function MyComp(props) {
const [Images,setImages] = useState([]) // in this array I store the recieved image's URL
const [isBlocking,setIsBlocking] = useState(true) // used for prompt
const handleSubmit = (event) => {
event.preventDefault();
setIsBlocking(false)
}
return(
<Grid container className={classes.mainGrid} direction="row" spacing={2}>
<Grid item xs={4} xl={4}>
<Prompt when={isBlocking} message="There are unsaved datas. Are you sure you want to leave?"/>
<form className={classes.form} onSubmit={handleSubmit}>
... somecode
</Grid>
</Grid>
)
}
export default MyComp
Thanks in advance

Inside React Function Component you can call the prompt when the user is trying to leave , i.e when the component is unmounting
In Class Component of React you can use React componentWillUnmount() and in Function Component You can use useEffect cleanup function
Code for Function Component
import React, { useEffect } from "react";
import { Link } from "react-router-dom";
export default function Home(props) {
useEffect(() => {
return function cleanup() {
alert("unmounting");
//call api and execute your code here
};
}, []);
return (
<div>
<Link to="/home">
On Click I will unmount this component and go to /home
</Link>
</div>
</Link>
);
}
Code for Class Component
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export default class Test extends Component {
componentWillUnmount() {
alert('unmounting component');
//call your code here
}
render() {
return (
<div>
<Link to='/home'>
On Click I will unmount this component and go to /home
</Link>
</div>
);
}
}
You can check this codesandbox if you want any ref

When you leave the page, the component method componentWillUnmount() fires. I don't recall how this behaves if you were to simply close the browser window as opposed to just navigating away, nor do I recall how you can escape it and stay on the component, but that should at least be a good starting point for you. Obviously you'd have to do a class extending React.Component for this one instead of a straight function.

Related

React Rendered page is 1 page behind requested page

I have a state in Redux called page and is changed whenever I click a URL to another page. This is to make my website just have one URL. Whenever I request for a new page, it only gets updated on the next click. For example, when I click Login, nothing happens. Then when I click Register, the Login page renders. Then when I click About, the Register page renders. Why is the page rendering 1 page slow? Is there any way to fix this? I want to make a fade animation to the page and then make the new page appear. Is this something to do with having to use nextProps? I've heard about it somewhere but I neither know what it is nor know how to use it.
import React, { Component } from 'react'
// Redux
import { connect } from "react-redux"
// Components
import Login from './routes/Login'
import About from './routes/About'
import Register from './routes/Register'
import Chat from './routes/Chat'
// PropTypes
import PropTypes from 'prop-types'
class Renderer extends Component {
state = {
rendered: <About /> // Default page to render
}
static propTypes = {
page: PropTypes.string.isRequired
}
componentDidUpdate(prevProps) {
const main = document.getElementsByTagName('main')[0] // Where the rendered page should be
if (this.props.page !== prevProps.page) { // If page state changes
main.style.animation = "fadeOut 0.5s forwards" // Fade old page
setTimeout(() => { // After faded
let returned
switch (this.props.page) {
case 'About':
returned = <About />
break
case 'Login':
returned = <Login />
break
case 'Register':
returned = <Register />
break
case 'Chat':
returned = <Chat />
break
default:
returned = <About />
}
this.state.rendered = returned
main.style.animation = "fadeIn 0.5s forwards" // Show new page
}, 500)
}
}
render() {
return this.state.rendered
}
}
const mapStateToProps = state => ({
page: state.page
})
export default connect(mapStateToProps)(Renderer)
Is there a way to render data in the componentDidUpdate() instead of putting it in the state then updating it? Because I think that is the main cause of the problem
I'm guessing that you should call this.setState and not this.state.rendered = returned. Furthermore the animation isn't going to be right this way. I think it works if you call the animation inside the setState callback, which should happen after the component is re-rendered ( see reactjs.org/docs/react-component.html#setstate )

How to print another component when i logout. React and Firebase

How I can render another component when i logout on firebase.
I´m trying to re-render the page to print the LoginPage
This is my LoginPage that is render when I loggin with another form.
import React, { Component } from "react";
/*Importing firebase*/
import firebase from "firebase";
/*Importing pages*/
import LoginPage from "../login/LoginPage";
/*Importing components*/
import Header from "./containers/HandleHeader";
class IndexPage extends Component {
shouldComponentUpdate() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
} else {
this.forceUpdate();
return <LoginPage />;
}
});
}
render() {
return (
<div>
<Header />
</div>
);
}
}
export default IndexPage;
And this is my handleLogout that work when I click my logout button.
handleLogout = e => {
firebase
.auth()
.signOut()
.then(() => this.forceUpdate());
};
I want to make that when I logout I don´t need reload the page.
Usually the best way to do this is to maintain the logged-in state somewhere, then protect the entry points to any components that require authentication with logic like this:
render() {
const { loggedIn } = this.props;
if (!loggedIn) return <Redirect to="/login" />;
// Reset of component rendered below this point
}
Note that this logic can be in the component itself, some parent component, or some higher order component. The key is to have it somewhere that will prevent access to any protected component by redirecting in the render method before any protected information can be reached.
Redirecting is often achieved using some routing package like, say, react-router-dom, to navigate around. This means that when you log out, a user is implicitly always redirected because they can no longer access the protected components anymore.

Render a new Component without using router

I've created an application to React, and when it starts, the App component is rendered. I would like that when the user clicks on a button or link, the button or link has to be in the App component when clicking on that link, another component will be rendered but not inside the App component but only the new component will be rendered in the same URL. As for this new component, it has to have a similar button so that when the user clicks, only the App component is rendered and this component that the user has clicked on is not rendered, only the App component.
I do not know if I explained myself correctly. Ask me any question if you need some clarification.
My App component is the following:
import React, { Component } from 'react';
import Touch from './Touch';
import '../App.css';
class App extends Component{
render() {
return(
<div>
<div className="wrapper" >
<button >NewComponent</button><NewComponent />???
<h1>Google Cloud Speech with Socket.io</h1>
<p id="ResultText"><span className="greyText">No Speech to Text yet</span></p>
</div>
<div className="buttonWrapper" >
<button className="btn" id="startRecButton" type="button"> Start recording</button>
<button className="btn" id="stopRecButton" type="button"> Stop recording</button>
</div>
</div>
);
}
}
export default App
My index.js is the following:
import React from 'react';
import ReactDOM from 'react-dom';
import './App.css';
import App from './components/App.js';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
If you really don't want to use react-router you will need to store a value in the component's state and change the rendering method to reflect which button was pressed. If you want each of those component to include the button you need to switch, do the following :
class App extends Component {
constructor(props){
super(props);
this.state = {renderA: false,};
}
handleClick = (event) => {
this.setState((prevState) => ({renderA: !prevState.renderA}));
};
render = () => {
return(
<div>
{this.state.renderA ?
<ComponentA handleClick={this.handleCLick}/>:
<ComponentB handleClick={this.handleCLick}/>
}
</div>
);
};
} export default App;
// ComponentA
class ComponentA extends Component {
render = () => {
return(
<div>
// what you want inside your first page here
<button onClick={this.props.handleClick}
</div>
);
}
} export default ComponentA;
// ComponentB
class ComponentB extends Component {
render = () => {
return(
<div>
// what you want inside your second page here
<button onClick={this.props.handleClick}
</div>
);
}
} export default ComponentB;
But using react-router might also suits your case, and if you are going to write a large app, you should use it instead of rendering differents children components within the same one, based on users inputs.
If the URL stay the same, I don't think React-Router might help you.
If you want that App Component is not loaded, I think you should create two more Component, a Wrapper one, and the new component you want to display (from now on newComponent). What I suggest is:
Creating a property isButtonClicked inside the state of the Wrapper Component;
Creating a function handleButtonClick() inside the Wrapper Component:
handleButtonClick() => {
let isButtonClicked = !this.state.isButtonClicked;
this.setState({ isButtonClicked });
}
In the render() method of the Wrapper component, you write something like this:
render() {
if (this.state.isButtonClicked)
return <App />
else
return <NewComponent />
}
Then, in both App and NewComponent, if you click on the button, you call the this.props.handleButtonClick(), which will lead to a change of the state of Wrapper Component, therefore to a change of what is shown on the screen.

React.js setState does not update state even if render method was called

I cannot believe that I cannot update state by setState.
I want to update cardModalOpen state to close the Modal.
I add bind(this) but it still does not work.
(Modal is opened by click Card Component)
However, I did setState({cardModalOpen: false}) by closeModal() function but it is still true even after render method was called.
Can someone please explain what I am doing wrong.
This is my code.
index.js
import React, { Component }from 'react';
import { Button, Card, Image, Header, Modal, Form, Input } from 'semantic-ui-react'
class App extends React.Component {
state = { cardModalOpen:false }
showCardModal() {
this.setState({cardModalOpen:true})
}
closeModal(){
this.setState({cardModalOpen:false})
}
render() {
const messagesDataNew = [];
for (var i = 0; i < 3; i++) {
messagesDataNew.push(
<Card
onClick={() => {
this.showCardModal();
}}
>
<DetailModal
cardModalOpen={this.state.cardModalOpen}
closeModal={this.closeModal}
/>
</Card>
);
}
return <div>{messagesDataNew}</div>;
}
}
DetailModal.js
import React, { Component }from 'react';
import { Button, Card, Image, Header, Modal, Form, Input } from 'semantic-ui-react'
class DetailModal extends Component{
render(){
return(
<Modal open={this.props.cardModalOpen} onClose={()=>{this.props.closeModal()}} >
<Modal.Header>Select a Photo</Modal.Header>
<Modal.Content image>
<Image wrapped size='medium' src='https://react.semantic-ui.com/images/avatar/large/rachel.png' />
<Modal.Description>
<Header>Default Profile Image</Header>
<p>We've found the following gravatar image associated with your e-mail address.</p>
<p>Is it okay to use this photo?</p>
</Modal.Description>
</Modal.Content>
<Button onClick={()=>{this.props.closeModal()}}>Close</Button>
</Modal>
)
}
}
export default DetailModal;
Here is a codesandbox with issue reproduced https://codesandbox.io/s/jjk7nw647y
In codesandbox you shared there is no clickable trigger-element for any of the modals. That's because of you are rendering an empty content of Card.
Here is my changes to your example https://codesandbox.io/s/l97n95n2om
The only difference is line #20 - I added a text Some text so your Card component has valid visible (and clickable) DOM element.
Do not forget bind your state handler functions:
constructor(props){
super(props)
this.showCardModal=this.showCardModal.bind(this)
this.closeModal =this.closeModal.bind(this)
}

Add loader on button click in react/redux application

I'm trying to add a Loader as Higher-Order-Component on button click in react/redux application.
Already have working Loader component and styling, just need to set logic when button is clicked show loader and hide existing button.
Button component:
import React from 'react'
import '../../../styles/components/_statement-print.scss';
import Loader from './Loader';
const StatementPrint = (props) => {
return (
<div>
<button
className="print-statement-button"
onClick={props.handleStatementPrint}>PRINT
</button>
</div>
);
};
export default Loader(StatementPrint);
Loader:
import React, { Component} from 'react';
import '../../../styles/components/_loader.scss';
const Loader = (WrappedComponent) => {
return class Loader extends Component {
render() {
return this.props.handleStatementPrint // Where must be logic when to show loader or existing button component
? <button className="loader-button">
<div className="loader">
<span className="loader-text">LOADING...</span>
</div>
</button>
: <WrappedComponent {...this.props} />
}
}
}
export default Loader;
In Loader component i added comment where need to write logic when to set loader or button.
I followed this example: ReactCasts - Higher Order Components
I searched a lot of examples but most of them shows how to set loader then is data is fetching, but in my case i just need to show then onClick method is triggered.
So how to set logic when onClick method is fired? Is this is a good aproach? Also it will be better to try acomplish this doing with redux state, but don't know how to do this.
Any help will be appreciated.
You will have to make small modifications to achieve what you want.
The wrapper component Loader can have a isLoading state, on the basis of which you can decide whether to show the loader span or the wrapped component.
This state isLoading can be updated by the wrapped component by passing showLoader function as a prop.
Button component
import React from 'react'
import '../../../styles/components/_statement-print.scss';
import Loader from './Loader';
const StatementPrint = ({handleStatementPrint, showLoader}) => {
return (
<div>
<button
className="print-statement-button"
onClick={() => {
showLoader();
handleStatementPrint();
}}>
PRINT
</button>
</div>
);
};
export default Loader(StatementPrint);
Loader
import React, { Component} from 'react';
import '../../../styles/components/_loader.scss';
const Loader = (WrappedComponent) => {
return class Loader extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: false
}
this.showLoader = this.showLoader.bind(this);
}
showLoader() {
this.setState({isLoading: true});
}
render() {
return this.state.isLoading
? <button className="loader-button">
<div className="loader">
<span className="loader-text">LOADING...</span>
</div>
</button>
: <WrappedComponent
{...this.props}
showLoader={this.showLoader}
/>
}
}
}
export default Loader;
EDIT
Since handleStatementPrint was required to be called, I have updated the click handler to include that function.
Also using de-structuring to avoid typing props repeatedly. See here for more info.
Just some external state is needed.
If you can't have external state (eg isLoading) than you could pass a function into a loader hoc which will derive isLoading from current props
Example: https://codesandbox.io/s/8n08qoo3j2

Categories

Resources