The Context:
A hook is defined and returns an object that contains the timestamp of the last time a file was modified.
I calculate the difference from the timestamp until now to show the user how long it was been since they have last saved.
const StageFooter = (props) => {
const [, , meta] = useMetadata("Tenant Setup Data", "setupData")
return (
<StageControls>
<div id="footer-start"></div>
<SavingBlock key={meta?.modified}>
{`Last saved ${
meta.modified !== undefined ? formatDistanceToNow(meta.modified) : " "
} ago`}
</SavingBlock>
<div id="footer-end"></div>
</StageControls>
)
}
export default StageFooter
The Problem:
The calculated difference from the timestamp until now does not update in real-time. For example, it would say "Last saved 10 minutes ago" but after a couple of minutes has passed the string still remains the same. It would only update if the user navigates away from the page then back or if the user refreshes the page.
With all this in mind, I'm basically looking to rerender the component every time a minute has passed so that the value is updated in real-time.
Thanks for your time!
4 methods to force a re-render in React 1. Re-render component when state changes Any time a React component state has changed, React has to run the render ()... 2. Re-render component when props change class Child extends React.Component { render() { console.log('Child component:... 3. Re-render ...
React schedules a render every time state changes (scheduling a render doesn’t mean this happens immediately, this might take time and be done at the best moment). Changing a state means React triggers an update when we call the useState function (useState is a Hook that allows you to have state variables in functional components).
Update in prop: Likewise the change in prop leads to state change and state change leads to re-rendering of the component by React. Re-rendering of parent component: Whenever the components render function is called, all its subsequent child components will re-render, regardless of whether their props have changed or not.
It triggers a re-render when the state changes. The first time you click the button you change the state from false to true so a rerender is triggered. Subsequent clicks you change it from true to true which isn't a change, so it doesn't. so it actually changes.
You can create a effect that calls setTimeout
every minute and when displaying the time, just subtract from the date.
You should also make a separete component for this, so if you use this inside a component, it won't rerender the hole component "every minute" and only rerender the text "x minutes since last change"
const [fakeCurrentDate, setFakeCurrentDate] = useState(new Date()) // default value can be anything you want
useEffect(() => {
setTimeout(() => setFakeCurrentDate(new Date()), 60000)
}, [fakeCurrentDate])
...
{/* display time passed since*/}
<div>{fakeCurrentDate - modifiedDate}</div>
Working codesandbox (you will need to wait a minute to see changes)
But as Sterling Archer said in the comments, is this good? Well... who knows?
Another approach for this would be show a message Updated at XX:XX time
instead of showing to the user how many minutes has passed. But this is much more about UX than the technology
I suggest keeping track of the time passed since the initial mount of the component in a state and updating it with an interval. We then display the time passed considering the initial value and adding the time passed since that initial value was received (since the component was mounted).
export const Time = () => {
// This would be the initial time. We can get it from a hook
// (meta.modified in your case) or for this example from a ref.
// I'm setting this to 2000 meaning it was updated 2 seconds ago.
const initial_time = useRef(2000);
const [time_passed, setTimePassed] = useState(0);
useEffect(() => {
const interval_time = 1000;
// Set an interval that will update every interval_time
const myInterval = setInterval(
() => setTimePassed(time => time + interval_time),
interval_time
);
// Clear the interval when the component unmounts
return () => clearInterval(myInterval);
}, []);
return initial_time.current + time_passed;
};
In order to display the time passed in a friendly way check this other question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With