Reputation: 453
I'm struggling on rendering React component depending to result of condition. To be more accurate I'm working on Alarm clock App where you can set time when alarm should 'ring'.
There are 3 functions which are updating every second thru useEffect().
currentTime() -gets current time and inserts in state
function currentTime(){
let time = new Date().toLocaleTimeString('it-IT');
let hours = time.substr(0,2);
let minutes = time.substr(3,2);
let seconds = time.substring(time.length-2)
setTime(prevState => ({
...prevState,
currentHour:hours,
currentMinute:minutes,
currentSecond:seconds
}))
}
currentDate() -gets current date and inserts in state
function currentDate(){
let date = new Date();
let day = days[date.getDay()];
setTime(prevState => ({
...prevState,
currentDay:day
}))
}
checkTime() -checks if alarmHours state if empty or not. This state has value only if Alarm is set.
function checkTime(){
if(time.alarmHours && time.alarmMinutes){
if(time.currentHour === time.alarmHours && time.currentMinute === time.alarmMinutes &&
time.currentSecond === '00'){
setShowRing(true);
}
}
}
To set an Alarm you have to click on SET ALARM button ,which is for now, located in top right corner. After click ,there comes Alarm.js component ,which fades in and when you are done fades out. These transitions are done using React Spring Transitions.
To this Alarm.js component is passed function as a prop from App.js. This function is supposed to get all of the filled information from Alarm.js (message for alarm and time when alarm should ring) and store them in state.
function getAlarm(){
let description = document.getElementById('text').value;
let hours = document.getElementById('hours').value;
let minutes = document.getElementById('minutes').value;
setTime(prevState => ({
...prevState,
message:description,
alarmHours:hours,
alarmMinutes:minutes,
}));
}
In my previous question I was asking how to call this Ring component when condition is fulfilled and current time is equal to time set. I was told to do this :
const [showRing,setShowRing] = useState(false);
Then when checkTime condition is fulfilled
setShowRing(true);
And in App.js render :
{showRing && <Ring message={time.message} turnOff={()=>setShowRing(false)} />}
It works. But without transitions and would be more happier if it's completely separated in it's own file just like Alarm.js. I've tried to add transitions to it ,but then it didn't even display. The best possible option will be to call it like Alarm.js in condition and all of these transitions do in it's own file. I mean
function checkTime(){
if(time.alarmHours && time.alarmMinutes){
if(time.currentHour === time.alarmHours && time.currentMinute === time.alarmMinutes &&
time.currentSecond === '00'){
return <Ring />
}
}
}
But it doesn't work. Even if I try to add those transitions to it in Ring.js. Is there anybody who can help me with this ,please? I would really appreciate that.
CODESANDBOX here.
Upvotes: 0
Views: 369
Reputation: 9354
You're in a bit of a bind here because if you render the component conditionally in App
, the transition leave in Ring
is not going to have enough time to complete when the component is unmounted. Either the transition logic has to live in App
, or you have to render Ring
persistently like you already do with Alarm
. It also needs to be tied to the showRing
state via props - it can't control its own state because it depends on the parent.
I'd suggest persistently rendering the Ring
component like this:
<Ring message={time.message} showRing={showRing} turnOff={() => setShowRing(false)} />
And inside it:
function Ring(props) {
const { message, turnOff, showRing } = props;
const ringTransitions = useTransition(showRing, null, {
from: { position: "absolute", opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 }
});
return (
<div>
{ringTransitions.map(
({ item, key, props }) =>
item && (
<animated.div key={key} style={props} className="overlay">
<div className="inputBox">
<h3>{message}</h3>
<a onClick={turnOff}>TURN OFF</a>
</div>
</animated.div>
)
)}
</div>
);
}
Upvotes: 1