I am trying to create a custom toast to show error notification. Toast visibility is depends on the isShowed
from the props.
export type ToastProps = {
color: 'error' | 'success' | 'warning';
message: string;
isShowed: boolean;
};
const Toast = (props: ToastProps) => {
const { color, message, isShowed } = props;
const [showed, setShowed] = useState(false);
console.log(isShowed, showed);
useEffect(() => {
if (isShowed) {
setShowed(true);
} else {
setShowed(false);
}
}, [isShowed]);
return (
<div
className="toast-notification-wrapper"
style={{
position: 'fixed',
top: '20px',
left: '50%',
transform: 'translateX(-50%)',
zIndex: 9999,
padding: '15px',
borderRadius: '5px',
transition: 'all 0.3s ease-out',
opacity: showed ? 1 : 0,
pointerEvents: showed ? 'auto' : 'none',
display: 'flex',
justifyContent: 'space-between',
minWidth: '150px',
backgroundColor:
color === 'error'
? '#dc3545'
: color === 'success'
? '#28a745'
: '#ffc107',
}}
>
<p style={{ margin: 0, color: 'white', fontSize: '15px', lineHeight: 1 }}>
{message}
</p>
<span
style={{
cursor: 'pointer',
display: 'flex',
alignContent: 'center',
justifyContent: 'center',
marginLeft: '30px',
}}
onClick={() => {
setShowed(!isShowed);
}}
>
<FontAwesomeIcon
icon={faClose}
style={{ color: 'white', fontSize: '15px' }}
/>
</span>
</div>
);
};
This is my toast components
const Login = (props: { isCompleted: boolean; isLoggedIn: boolean }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const navigate = useNavigate();
const [toast, setToast] = useState({
isShowed: false,
color: 'error',
message: 'failed',
} as ToastProps);
const { isCompleted, isLoggedIn } = props;
if (isCompleted === false) {
return null;
}
if (isLoggedIn) {
navigate('/');
}
const togglePasswordVisibility = () => {
setShowPassword(showPassword ? false : true);
};
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
await login({ username, password });
window.location.href = '/';
} catch (error: any) {
setToast({
isShowed: true,
color: 'error',
message:
error.response.data?.message ||
'Unexpected Error. Please try again later',
});
}
};
return (
<div className="login-form">
<Toast
isShowed={toast.isShowed}
color={toast.color}
message={toast.message}
></Toast>
<div className="login-form-container">
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<div className="form-row">
<label>Username</label>
<input
type="text"
placeholder="Enter your username"
required={true}
onChange={e => setUsername(e.target.value)}
></input>
</div>
<div className="form-row">
<label>Password</label>
<input
type={showPassword ? 'text' : 'password'}
placeholder="********"
required={true}
onChange={e => setPassword(e.target.value)}
></input>
<span
onClick={togglePasswordVisibility}
style={{
position: 'absolute',
right: '20px',
top: 'calc(50% + 12px)',
fontSize: '13px',
transform: 'translateY(-50%)',
cursor: 'pointer',
}}
>
<FontAwesomeIcon
icon={showPassword ? faEyeSlash : faEye}
style={{ color: 'black' }}
/>
</span>
</div>
<div className="button-wrapper">
<button value={'Login'}>Login</button>
</div>
</form>
</div>
</div>
);
};
const Login = (props: { isCompleted: boolean; isLoggedIn: boolean }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const navigate = useNavigate();
const [toast, setToast] = useState({
isShowed: false,
color: 'error',
message: 'failed',
} as ToastProps);
const { isCompleted, isLoggedIn } = props;
if (isCompleted === false) {
return null;
}
if (isLoggedIn) {
navigate('/');
}
const togglePasswordVisibility = () => {
setShowPassword(showPassword ? false : true);
};
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
await login({ username, password });
window.location.href = '/';
} catch (error: any) {
setToast({
isShowed: true,
color: 'error',
message:
error.response.data?.message ||
'Unexpected Error. Please try again later',
});
}
};
return (
<div className="login-form">
<Toast
isShowed={toast.isShowed}
color={toast.color}
message={toast.message}
></Toast>
<div className="login-form-container">
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<div className="form-row">
<label>Username</label>
<input
type="text"
placeholder="Enter your username"
required={true}
onChange={e => setUsername(e.target.value)}
></input>
</div>
<div className="form-row">
<label>Password</label>
<input
type={showPassword ? 'text' : 'password'}
placeholder="********"
required={true}
onChange={e => setPassword(e.target.value)}
></input>
<span
onClick={togglePasswordVisibility}
style={{
position: 'absolute',
right: '20px',
top: 'calc(50% + 12px)',
fontSize: '13px',
transform: 'translateY(-50%)',
cursor: 'pointer',
}}
>
<FontAwesomeIcon
icon={showPassword ? faEyeSlash : faEye}
style={{ color: 'black' }}
/>
</span>
</div>
<div className="button-wrapper">
<button value={'Login'}>Login</button>
</div>
</form>
</div>
</div>
);
};
and this is my login form components
The toast notification worked the first time when error event triggerd. But when the error occurred again, the toast remained invisible. When I use the console.log to check the state, the isShowed
from props is true, but the showed
is false. Please help me. Thanks in advance.