Reputation: 519
I am trying to make a notification toast component. And I want it to be removed after 2 seconds (not-shown on the screed) Although, it is removed (not-shown on the screen via top:-100 argument), the component is getting rendered infinitely. You can see it from the console.log's I have placed inside the component and inside the useEffect
call with setTimeout
.
My expectation is that setTimeout
should run setShowState
after 2 seconds and then useEffect
should do the cleanup and remove the timer. So everything is back to normal until showState
changes.
import React, {useEffect, useState} from 'react'
import I18n from '../../i18n'
import styled from 'styled-components'
import {createGlobalStyle} from 'styled-components'
import {useSelector} from 'react-redux'
const NotificationStyle = createGlobalStyle`
@media (max-width: 500px) {
.notification_mssg {
left: 10px;
}
}
`
const Container = styled.div`
color: white;
position: fixed;
top: ${(props) => props.top}px;
right: 16px;
z-index: 2000;
transition: top 0.5s ease;
`
const NoticitactionIcon = styled.div`
float: left;
font-size: 27px;
width: 40px;
height: 40px;
text-align: center;
`
const NotificationMessage = styled.span`
padding: 10px;
line-height: 40px;
`
function NotificationAlertRoot(props) {
const create_notification = useSelector((state) => state.notifications.create_notification)
const {message, code} = create_notification.success_info
const [showState, setShowState] = useState({top: -100, msg: message, bgColor: '#444'})
// show notification
useEffect(() => {
setShowState({top: 96, msg: I18n.t(message), bgColor: backgroundColor(code)})
}, [message, code])
console.log('amIrendered', showState) // although showState doesn't change, component is getting rendered infinitely :/
// hide notification after 2 seconds
useEffect(() => {
const timerId = setTimeout(() => {
setShowState({
top: -100,
msg: '',
bgColor: `#ffffff00`,
})
console.log("timerId", timerId) // I see timerId is changing so the problem most probably in this useEffect call.
}, 2000)
return () => {
clearTimeout(timerId)
}
}, [showState])
const notificationIcon = (bgColor) => {
switch (bgColor) {
case '#32c786':
return (
<NoticitactionIcon style={{background: '#2aa872'}}>
<i className="zmdi zmdi-info" />
</NoticitactionIcon>
)
case '#ffc721':
return (
<NoticitactionIcon style={{background: '#fabb00'}}>
<i className="zmdi zmdi-alert-triangle" />
</NoticitactionIcon>
)
case '#ff6b68':
return (
<NoticitactionIcon style={{background: '#ff4642'}}>
<i className="zmdi zmdi-alert-circle" />
</NoticitactionIcon>
)
default:
return <span></span>
}
}
function backgroundColor(code) {
switch (Math.floor(code / 100)) {
case 2:
return '#32c786'
case 3:
return '#ffc721'
case 4:
return '#ff6b68'
case 5:
return '#ff6b68'
default:
return '#444'
}
}
return (
<React.Fragment>
<NotificationStyle />
<Container
className="notification_mssg"
top={showState.top}
style={{background: showState.bgColor}}
>
{notificationIcon(showState.bgColor)}
<NotificationMessage>{showState.msg}</NotificationMessage>
</Container>
</React.Fragment>
)
}
export default NotificationAlertRoot
Do you have an idea what is wrong above?
Upvotes: 0
Views: 86
Reputation: 335
I guess the problem comes from your dependency array. Your useEffect
is dependent on showState
and each time, you are calling setShowState
in your useEffect
when setShowState
is called showState
changes and then again, your useEffect
gets invoked(it is dependent on ShowState
), and again setShowState
is called and ...
infinity loop!
Upvotes: 1
Reputation: 519
I found the root of the problem. Sometimes you are too focused on something and you forget the little details of useEffect. It is always dangerous to provide objects as dependency arrays to useEffect. The dependency array values should be simple values. So now I introduced a new state (flag, setFlag) with just boolean values and I make the second useEffect just to follow that simple value. Everything is working just fine now.
import React, {useEffect, useState} from 'react'
import I18n from '../../i18n'
import styled from 'styled-components'
import {createGlobalStyle} from 'styled-components'
import {useSelector} from 'react-redux'
const NotificationStyle = createGlobalStyle`
@media (max-width: 500px) {
.notification_mssg {
left: 10px;
}
}
`
const Container = styled.div`
color: white;
position: fixed;
top: ${(props) => props.top}px;
right: 16px;
z-index: 2000;
transition: top 0.5s ease;
`
const NotificationIcon = styled.div`
float: left;
font-size: 27px;
width: 40px;
height: 40px;
text-align: center;
`
const NotificationMessage = styled.span`
padding: 10px;
line-height: 40px;
`
function NotificationAlertRoot(props) {
const create_notification = useSelector((state) => state.notifications.create_notification)
const {message, code} = create_notification.success_info
const [showState, setShowState] = useState({top: -100, msg: message, bgColor: '#444'})
const [flag, setFlag] = useState(false) // when you follow the showState at the second useEffect you have an infinite loop. Because it is an object.
// show notification
useEffect(() => {
setShowState({top: 96, msg: I18n.t(message), bgColor: backgroundColor(code)})
setFlag(true)
}, [message, code])
// hide notification after 2 seconds
useEffect(() => {
const timerId = setTimeout(() => {
setShowState({top: -100,msg: '', bgColor: `#ffffff00`})
setFlag(false)
}, 2000)
return () => {
clearTimeout(timerId)
}
}, [flag]) // showState
const notificationIcon = (bgColor) => {
switch (bgColor) {
case '#32c786':
return (
<NotificationIcon style={{background: '#2aa872'}}>
<i className="zmdi zmdi-info" />
</NotificationIcon>
)
case '#ffc721':
return (
<NotificationIcon style={{background: '#fabb00'}}>
<i className="zmdi zmdi-alert-triangle" />
</NotificationIcon>
)
case '#ff6b68':
return (
<NotificationIcon style={{background: '#ff4642'}}>
<i className="zmdi zmdi-alert-circle" />
</NotificationIcon>
)
default:
return <span></span>
}
}
const backgroundColor = (code) => {
switch (Math.floor(code / 100)) {
case 2:
return '#32c786'
case 3:
return '#ffc721'
case 4:
return '#ff6b68'
case 5:
return '#ff6b68'
default:
return '#444'
}
}
return (
<React.Fragment>
<NotificationStyle />
<Container
className="notification_mssg"
top={showState.top}
style={{background: showState.bgColor}}
>
{notificationIcon(showState.bgColor)}
<NotificationMessage>{showState.msg}</NotificationMessage>
</Container>
</React.Fragment>
)
}
export default NotificationAlertRoot
Upvotes: 0