Delayed rendering of React components
I have created Delayed component using Hooks and TypeScript
import React, { useState, useEffect } from 'react';
type Props = {
children: React.ReactNode;
waitBeforeShow?: number;
};
const Delayed = ({ children, waitBeforeShow = 500 }: Props) => {
const [isShown, setIsShown] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setIsShown(true);
}, waitBeforeShow);
return () => clearTimeout(timer);
}, [waitBeforeShow]);
return isShown ? children : null;
};
export default Delayed;
Just wrap another component into Delayed
export const LoadingScreen = () => {
return (
<Delayed>
<div />
</Delayed>
);
};
Another approach for a delayed component:
Delayed.jsx
:
import React from 'react';
import PropTypes from 'prop-types';
class Delayed extends React.Component {
constructor(props) {
super(props);
this.state = {hidden : true};
}
componentDidMount() {
setTimeout(() => {
this.setState({hidden: false});
}, this.props.waitBeforeShow);
}
render() {
return this.state.hidden ? '' : this.props.children;
}
}
Delayed.propTypes = {
waitBeforeShow: PropTypes.number.isRequired
};
export default Delayed;
Usage:
import Delayed from '../Time/Delayed';
import React from 'react';
const myComp = props => (
<Delayed waitBeforeShow={500}>
<div>Some child</div>
</Delayed>
)
In your father component <Father />
, you could create an initial state where you track each child (using and id for instance), assigning a boolean value, which means render or not:
getInitialState() {
let state = {};
React.Children.forEach(this.props.children, (child, index) => {
state[index] = false;
});
return state;
}
Then, when the component is mounted, you start your timers to change the state:
componentDidMount() {
this.timeouts = React.Children.forEach(this.props.children, (child, index) => {
return setTimeout(() => {
this.setState({ index: true; });
}, child.props.delay);
});
}
When you render your children, you do it by recreating them, assigning as a prop the state for the matching child that says if the component must be rendered or not.
let children = React.Children.map(this.props.children, (child, index) => {
return React.cloneElement(child, {doRender: this.state[index]});
});
So in your <Child />
component
render() {
if (!this.props.render) return null;
// Render method here
}
When the timeout is fired, the state is changed and the father component is rerendered. The children props are updated, and if doRender
is true
, they will render themselves.
I think the most intuitive way to do this is by giving the children a "wait" prop
, which hides the component for the duration that was passed down from the parent. By setting the default state to hidden, React will still render the component immediately, but it won't be visible until the state has changed. Then, you can set up componentWillMount
to call a function to show it after the duration that was passed via props.
var Child = React.createClass({
getInitialState : function () {
return({hidden : "hidden"});
},
componentWillMount : function () {
var that = this;
setTimeout(function() {
that.show();
}, that.props.wait);
},
show : function () {
this.setState({hidden : ""});
},
render : function () {
return (
<div className={this.state.hidden}>
<p>Child</p>
</div>
)
}
});
Then, in the Parent component, all you would need to do is pass the duration you want a Child to wait before displaying it.
var Parent = React.createClass({
render : function () {
return (
<div className="parent">
<p>Parent</p>
<div className="child-list">
<Child wait={1000} />
<Child wait={3000} />
<Child wait={5000} />
</div>
</div>
)
}
});
Here's a demo