displaying Flash message in react - reactjs

My task is to display flash message("successfully created") on clicking the submit button.[On clicking the submit button , data will be stored in the server]I have run this command npm i react-flash-message.
<form onSubmit={this.handleSubmit}>
<input type="submit" value="Submit" />
</form>
handleSubmit function:
handleSubmit(event) {
fetch('url', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: this.state.name,
description: this.state.description
})
}).then(res => {
return res.json()
})
.then(data => console.log(data))
.then(() => {
window.location.reload(false)
/* return (
<div>
<FlashMessage duration={5000}>
<strong>I will disapper in 5 seconds!</strong>
</FlashMessage>
</div>
) */
})
/* window.flash('record has been created successfully!', 'success') */
.catch(error => console.log('ERROR from create component'))
}
}
I have commented the code I have tried to display flash message. But it is not working. Please someone help me to display the flash message.

According to react-flash-message page , you should include the FlashMessage in your render. So you may need to have a flag variable to set as true when you want to show the FlashMessage
Example:
in your render :
<form onSubmit={this.handleSubmit}>
<input type="submit" value="Submit" />
{ this.state.showMessage &&
<div>
<FlashMessage duration={5000}>
<strong>I will disappear in 5 seconds!</strong>
</FlashMessage>
</div>
}
</form>
handleSubmit function :
handleSubmit(event) {
this.setState({ showMessage: false });
fetch('url', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: this.state.name,
description: this.state.description
})
}).then(res => {
return res.json()
})
.then(data => console.log(data))
.then(() => {
this.setState({ showMessage: true });
})
/* window.flash('record has been created successfully!', 'success') */
.catch(error => console.log('ERROR from create component'))
}
}
main function if you are using class:
constructor(props) {
super(props);
this.state = {
showMessage: false
};
}
https://www.npmjs.com/package/react-flash-message

Easy peasy..
Whenever using onSubmit, dont forget to use event.preventDefault()..
And try using only one then block.
Now maintain a state variable, to set the status of the result. Once the result is fetched set the result status to true.
Render your FlashMessage component, when its true.
https://codesandbox.io/s/lucid-taussig-9x0o3

Related

React: call parent hook which results in child not being rendered safely

I am trying to implement JWT based authentication in React. I followed this tutorial from digitalocean, but used axios (which is promise based) instead of the fetch API.
If authentication fails, the error message is displayed as required, and on a successful login, the correct page is being displayed. React throws the following error however:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
I guess this is due to the fact that I call the loginpage parent's hook to set the JWT from within the .then() call from the axios promise, but the parent will stop rendering the loginpage and it's axios promise as soon as the jwt is set. How would I go about solving this neatly?
function App() {
const [token, setToken] = useState();
if(!token) {
return <EmployeeLogin setToken={setToken} />;
}
return (
<main>
<Switch>
<Route path="/someroute" component={someComponent} exact />
</Switch>
</main>
);
}
export default App;
// EmployeeLogin.jsx
const styles = theme => ({ ... });
class EmployeeLogin extends React.Component {
constructor(props) {
super(props);
const { setToken } = this.props;
this.state = {
email: '',
password: '',
error: null,
isLoading: false,
};
this.handleSubmitevents = this.handleSubmitevents.bind(this);
}
async handleSubmitevents(event) {
event.preventDefault();
const credentials = {
email: this.state.email,
password: this.state.password,
}
this.setState({
isLoading: true,
error: null,
});
axios.post('http://localhost:8080/account/employee/login', credentials, {
headers: {
"Content-Type": 'application/json', 'Accept': 'application/json'
}
})
.then(res => {
this.props.setToken(res.data.token); // Set the token to the parent (App.js)
})
.catch(error => {
this.setState({
error: error.response.data.message, // Show error message on failure
});
})
.then(() => {
this.setState({ isLoading: false }); // Always stop the loading indicator when done
});
}
componentWillUnmount() {
// How to terminate the promise neatly here?
}
render() {
const { classes } = this.props;
return (
<form className={classes.form} onSubmit={this.handleSubmitevents} noValidate>
<TextField
required
value={this.state.email}
onChange={(e) => { this.setState({ email: e.target.value }); }}
/>
<TextField
required
type="password"
value={this.state.password}
onChange={(e) => { this.setState({ password: e.target.value }); }}
/>
<Button type="submit">Sign In</Button>
</form>
);
}
}
EmployeeLogin.propTypes = {
classes: PropTypes.object.isRequired,
setToken: PropTypes.func.isRequired,
};
export default withStyles(styles)(EmployeeLogin);
Try to call setToken after setting isLoading to false so you can try something like below.
async handleSubmitevents(event) {
event.preventDefault();
const credentials = {
email: this.state.email,
password: this.state.password,
}
this.setState({
isLoading: true,
error: null,
});
let responseToken = ''; // set responseToken to blank before calling api
axios.post('http://localhost:8080/account/employee/login', credentials, {
headers: {
"Content-Type": 'application/json', 'Accept': 'application/json'
}
})
.then(res => {
responseToken = res.data.token; // set responseToken here
})
.catch(error => {
this.setState({
error: error.response.data.message, // Show error message on failure
});
})
.then(() => {
this.setState({ isLoading: false });
this.props.setToken(responseToken); // call props method from here
});
}

Username state gets undefined / null after a few minutes in JWT authentication React Django

I am making a React application with the backend of the Django REST framework. Everything works fine except when retrieving the username. Retrieving the username is no problem but keeping it for a time is a problem. I'm going to explain using comments now in the code;
import React, { Component } from 'react';
import Nav from './Components/Navbar';
import LoginForm from './Components/LoginForm';
import SignupForm from './Components/SignupForm';
import Layout from './Containers/Layout';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
displayed_form: '',
logged_in: localStorage.getItem('token') ? true : false,
username: '', // username state
error: null
};
}
componentDidMount() {
if (this.state.logged_in) {
fetch('http://localhost:8000/core/current_user/', { // fetch is used to get current user
headers: {
Authorization: `JWT ${localStorage.getItem('token')}`
}
})
.then(res => res.json())
.then(json => {
this.setState({ username: json.username }); // set the state
});
}
}
handle_login = (e, data) => {
e.preventDefault();
fetch('http://localhost:8000/token-auth/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(json => {
try {
localStorage.setItem('token', json.token);
this.setState({
logged_in: true,
displayed_form: '',
username: json.user.username,
error: '',
})
} catch (error) {
this.setState({error: "We Couldn't log you in, maybe theres a typo in the data you entered"})
this.handle_logout()
}
});
};
handle_signup = (e, data) => {
e.preventDefault();
fetch('http://localhost:8000/core/users/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(json => {
try {
localStorage.setItem('token', json.token);
this.setState({
logged_in: true,
displayed_form: '',
username: json.username,
})
if (this.state.username == 'A user with that username already exists.') {
this.setState({logged_in: false, username: '', error: 'Yikes, we could not create your account, you have used an existing username. Try again please...'})
this.handle_logout()
}
} catch (error) {
alert("Error")
}
});
};
handle_logout = () => {
localStorage.removeItem('token');
this.setState({ logged_in: false, username: '' });
};
display_form = form => {
this.setState({
displayed_form: form
});
};
render() {
let form;
switch (this.state.displayed_form) {
case 'login':
form = <LoginForm handle_login={this.handle_login} />;
break;
case 'signup':
form = <SignupForm handle_signup={this.handle_signup} />;
break;
default:
{
this.state.logged_in ?
form = null
:
form = <LoginForm handle_login={this.handle_login} />
}
}
return (
<div className="App">
<Nav
logged_in={this.state.logged_in}
display_form={this.display_form}
handle_logout={this.handle_logout}
/>
{form}
<h3>
{
this.state.logged_in && this.state.username !== undefined ?
<Layout username={this.state.username} />
: this.state.error ?
<p className="error">{this.state.error}</p>
: this.state.username == undefined ?
<div>
<p className="error" style={{maxWidth: 650, minHeight: 30, padding: 20}}>You exceeded the time being logged in, we take your account safety seriously. So please logout now and login again...</p>
{this.handle_logout}
</div>
:
<p></p>
}
</h3>
</div>
);
}
}
export default App;
If you read through everything, I have said that this.state.logged_in && this.state.username !== undefined ? show the layout component.
In that component, I have put an h1 with {this.state.username}.
The username gets shown but it becomes null/undefined after about 5 minutes. Even though, in the backend Django REST API, the current username can be seen.
I am very confused about why this is happening.
Update
I fixed this by using local storage to set a username and get the username. But, I want to know if this is safe.

How to clear the input after posting the data through fetch and making the code execute successful

I'm new to react, have created a chat using reactjs. I'm using "POST" request through fetch to post new message for a particular conversation. My Objective is when i send a message, then it should clear the input but unable to clear the input after sending message. Need to write for error also.. Anyone can help me in this?
myCode:
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
handleSend = event => {
const { phonNo } = this.props.phonNo;
event.preventDefault();
console.log("phone : " + "+" + phonNo);
this.setState({ toPhoneno: phonNo });
console.log("phonefkjdk : " + "+" + this.state.toPhoneno);
const data = {
toPhoneno: "+" + this.state.toPhoneno,
body: this.state.body
};
fetch("/api/messages", {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" }
})
.then(res => res.json())
.then(() => {});
};
render () {
return (
<div>
<input
className="sendMessage"
onChange={this.handleChange}
/>
<Button onClick={this.handleSend} className="sendBtn" icon>
<Icon name="send" color="blue" />
</Button>
</div>
);
}
Can anyone help me in this ? Thanks in advance
You can clear it end of the fetch Promise chain.
fetch("/api/messages", {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" }
})
.then(res => res.json())
.then(() => {
this.setState({ sendMessage: "" })
});
input should be controlled and it should have a name.
render () {
return (
<div>
<input
className="sendMessage"
name="sendMessage"
value={this.state.sendMessage}
onChange={this.handleChange}
/>
<Button onClick={this.handleSend} className="sendBtn" icon>
<Icon name="send" color="blue" />
</Button>
</div>
);
}

Passing data from fetch to another component

I have a Login component, which has form with inputs for user's data, and I have a method onFormSubmit with fetch. The problem is that I have no idea, how to pass token to another component ( it is there, I can console.log it ). The reason why I want to pass token to another component is that the other component is validating if user has logged in and by detecting token ( null = user didn't log in and redirect him to login page, otherwise go to protected pages )
My login.js component
class Login extends React.Component() {
constructor() {
super();
this.state = {
email: '',
password: '',
};
}
onInputChange = (e) => {
this.setState({
[e.target.id]: e.target.value
});
};
onFormSubmit = (e) => {
e.preventDefault();
fetch('http://localhost:3001/user/login', {
method: 'POST',
body: JSON.stringify({
email: this.state.email,
password: this.state.password
}),
headers: {
'Content-Type': 'application/json'
}
})
.then( res => {
if( res.status === 200){
this.props.history.push('/MyPlaces');
} else {
const error = new Error(res.error);
throw error;
}
})
.catch(err => {
console.error(err);
alert('Error login in please try again!');
});
};
render() {
return (
<div className="loginPanel">
<form onSubmit={this.onFormSubmit}>
<label>Email
<input type="text"
id="email"
value={this.state.email}
onChange={this.onInputChange}
/>
</label>
<label>Password
<input type="text"
id="password"
value={this.state.password}
onChange={this.onInputChange}/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
};
}
export default Login;
My authentication component
import React, {Component} from 'react';
import { Redirect } from 'react-router-dom';
export default function withAuth(ComponentToProtect, props) {
return class extends Component {
constructor() {
super();
this.state = {
loading: true,
redirect: false
};
}
componentDidMount() {
fetch('')
.then(res => {
if( res.status === 200) {
this.setState({ loading: false});
} else {
const error = new Error(res.error);
throw error;
}
})
.catch(err => {
console.error(err);
this.setState({ loading: false, redirect: true });
})
}
render() {
const { loading, redirect } = this.state;
if( loading ){
return null;
}
if( redirect ){
return <Redirect to="/Login" />;
}
return (
<React.Fragment>
<ComponentToProtect {...this.props}/>
</React.Fragment>
);
}
}
}
I know that there is nothing in fetch in authentication component, I thought that I should've make another api request ( same as in login component, then after fetch just invoke
.then(res => res.json()).then(res => {
let token = res.token;
console.log("token: ", token);
});
but it just doesn't seem to be good idea I think. Could you please give me some guide how may I do that?
You can simply use localStorage.setItem('token-name', token); to store the token after fetch. Then you can retrieve it using localStorage.getItem('token-name') across all the component. If the getItem returns null then you can simply redirect to login.
For logout, you can simply update the token value to null.
Login Component
fetch('http://localhost:3001/user/login', {
method: 'POST',
body: JSON.stringify({
email: this.state.email,
password: this.state.password
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
if( res.status === 200){
localStorage.setItem('token', res.token); //storing the token in localStorage.
this.props.history.push('/MyPlaces');
} else {
const error = new Error(res.error);
throw error;
}
})
.catch(err => {
console.error(err);
alert('Error login in please try again!');
});
Authentication Component
componentDidMount() {
let token = localStorage.getItem('token'); //retriving the token from localStorage
if(token === null){
this.setState({ loading: false, redirect: true });
}
}
Hope this will help you.

showing success and error messages in react/redux app

I'm trying to add toast notifications to my app, one plugin I've been trying to use is react-toastify.
The issue I'm having is probably more a general react/redux issue more than with a plugin such as react-toastify.
I'm using a reducer to set the redux state for errors and success messages, from what I understand with the current code, each error or success message is persistent in the store until another action is called to clear them.
The issue I can't figure out is how do I trigger a toast only once. Eg. I enter the wrong credentials, it creates an error toast, but whenever the state changes and reloads (typing anything into the email or password fields) it creates another toast.
How do I get it to only show once?
userActions.js
function handleErrors(res) {
if (res.ok) {
return res.json();
} else {
return res.json().then(err => {throw err;});
}
}
export const login = (user) => dispatch => {
fetch(`${url}/login`,
{
credentials: 'include',
method: 'post',
body: user,
headers: new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
})
.then(handleErrors)
.then(res =>
dispatch({
type: LOGIN,
payload: res
})
)
.catch(error =>
dispatch({
type: ERROR,
payload: error
})
)
}
userReducer.js
const initialState = {
errors: '',
success: ''
};
export default function(state = initialState, action) {
switch (action.type) {
case LOGIN:
return {
...state,
errors: '',
success: action.payload.message
};
case ERROR:
return {
...state,
success: '',
errors: action.payload.message
}
default:
return state;
}
}
app.js
app.post('/login', function(req, res) {
... return res.status(500).send({ message: 'Wrong credentials' });
... return res.status(200).send({ message: 'good!' });
});
login.js
class Login extends React.Component {
constructor() {
super();
this.state = {
email: "",
password: ""
}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
render() {
const { errors, login, success } = this.props;
if (success !== '') toast.success(success, {
position: toast.POSITION.TOP_CENTER
});
if (errors !== '') toast.error(errors, {
position: toast.POSITION.TOP_CENTER
});
return (
<div>
<input type="text" id="email" placeholder="Email Address" onChange={this.handleChange} />
<input type="password" id="password" placeholder="Password" onChange={this.handleChange} />
<button onClick={() => login(JSON.stringify({email: this.state.email, password: this.state.password}))}>Log In</button>
<ToastContainer />
</div>
)
}
}
const mapStateToProps = state => ({
errors: state.store.errors,
success: state.store.success
});
export default connect(mapStateToProps, {login})(Login);
You're calling toast.success or toast.error inside render which makes a new toast pop up every time you re-render the component.
The solution is simple. Move your toast calls outside render, where they will only be called once.
One way to achieve this is to return a value from your userAction.
export const login = (user) => dispatch => {
return new Promise((resolve, reject) => {
fetch(`${url}/login`,
{
credentials: 'include',
method: 'post',
body: user,
headers: new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
})
.then(handleErrors)
.then(res => {
dispatch({
type: LOGIN,
payload: res
})
resolve(res)
}
)
.catch(error => {
dispatch({
type: ERROR,
payload: error
})
reject(error)
}
)
}
}
Then use that value to toast in login.js.
class Login ... {
...
loginUser = () => {
this.props.login(JSON.stringify({email: this.state.email, password: this.state.password}))
.then(res => {
toast.success(res.message, { position: toast.POSITION.TOP_CENTER })
}
).catch(error => {
toast.error(error.message, { position: toast.POSITION.TOP_CENTER })
}
)
}
...
render() {
return (
...
<button onClick={this.loginUser}>Log In</button>
...
)
}
}
There are other ways to achieve the same functionality and depending on the structure of your project, you may want to toast in a more generalized way.

Resources